Android Tutorial - Media : Audio
Record audio to 3gpp file
package app.test; import java.io.File; import android.app.Activity; import android.media.MediaPlayer; import android.media.MediaRecorder; import android.os.Bundle; import android.os.Environment; import android.view.View; public class Test extends Activity { private MediaPlayer mediaPlayer; private MediaRecorder recorder; private String OUTPUT_FILE; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); OUTPUT_FILE = Environment.getExternalStorageDirectory() + "/recordaudio.3gpp"; } public void doClick(View view) { switch(view.getId()) { case R.id.beginBtn: try { beginRecording(); } catch (Exception e) { e.printStackTrace(); } break; case R.id.stopBtn: try { stopRecording(); } catch (Exception e) { e.printStackTrace(); } break; case R.id.playRecordingBtn: try { playRecording(); } catch (Exception e) { e.printStackTrace(); } break; case R.id.stopPlayingRecordingBtn: try { stopPlayingRecording(); } catch (Exception e) { e.printStackTrace(); } } } private void beginRecording() throws Exception { killMediaRecorder(); File outFile = new File(OUTPUT_FILE); if(outFile.exists()) { outFile.delete(); } recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); recorder.setOutputFile(OUTPUT_FILE); recorder.prepare(); recorder.start(); } private void stopRecording() throws Exception { if (recorder != null) { recorder.stop(); } } private void killMediaRecorder() { if (recorder != null) { recorder.release(); } } private void killMediaPlayer() { if (mediaPlayer != null) { try { mediaPlayer.release(); } catch (Exception e) { e.printStackTrace(); } } } private void playRecording() throws Exception { killMediaPlayer(); mediaPlayer = new MediaPlayer(); mediaPlayer.setDataSource(OUTPUT_FILE); mediaPlayer.prepare(); mediaPlayer.start(); } private void stopPlayingRecording() throws Exception { if(mediaPlayer != null) { mediaPlayer.stop(); } } @Override protected void onDestroy() { super.onDestroy(); killMediaRecorder(); killMediaPlayer(); } } //main.xml <?xml version="1.0" encoding="utf-8"?> <!-- This file is /res/layout/record.xml --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:id="@+id/beginBtn" android:text="Begin Recording" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="doClick" /> <Button android:id="@+id/stopBtn" android:text="Stop Recording" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="doClick" /> <Button android:id="@+id/playRecordingBtn" android:text="Play Recording" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="doClick" /> <Button android:id="@+id/stopPlayingRecordingBtn" android:text="Stop Playing Recording" android:layout_width="fill_parent" android:layout_height="wrap_content" android:onClick="doClick" /> </LinearLayout>
Audio Recording
package app.test; import android.app.Activity; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.os.Bundle; import android.util.Log; public class MainActivity extends Activity { private int mAudioBufferSize; private int mAudioBufferSampleSize; private AudioRecord mAudioRecord; private boolean inRecordMode = false; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initAudioRecord(); } @Override public void onResume() { super.onResume(); inRecordMode = true; Thread t = new Thread(new Runnable() { @Override public void run() { getSamples(); } }); t.start(); } protected void onPause() { inRecordMode = false; super.onPause(); } @Override protected void onDestroy() { if(mAudioRecord != null) { mAudioRecord.release(); } super.onDestroy(); } private void initAudioRecord() { try { int sampleRate = 8000; int channelConfig = AudioFormat.CHANNEL_IN_MONO; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; mAudioBufferSize = 2 * AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); mAudioBufferSampleSize = mAudioBufferSize / 2; mAudioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, mAudioBufferSize); } catch (IllegalArgumentException e) { e.printStackTrace(); } int audioRecordState = mAudioRecord.getState(); if(audioRecordState != AudioRecord.STATE_INITIALIZED) { finish(); } } private void getSamples() { if(mAudioRecord == null) return; short[] audioBuffer = new short[mAudioBufferSampleSize]; mAudioRecord.startRecording(); int audioRecordingState = mAudioRecord.getRecordingState(); if(audioRecordingState != AudioRecord.RECORDSTATE_RECORDING) { finish(); } while(inRecordMode) { int samplesRead = mAudioRecord.read(audioBuffer, 0, mAudioBufferSampleSize); } mAudioRecord.stop(); } }
Audio recoding and call back
package app.test; import android.app.Activity; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaRecorder; import android.media.AudioRecord.OnRecordPositionUpdateListener; import android.os.Bundle; import android.util.Log; public class Test extends Activity { private int mAudioBufferSize; private int mAudioBufferSampleSize; private AudioRecord mAudioRecord; private boolean inRecordMode = false; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initAudioRecord(); } @Override public void onResume() { super.onResume(); inRecordMode = true; Thread t = new Thread(new Runnable() { @Override public void run() { getSamples(); } }); t.start(); } protected void onPause() { inRecordMode = false; super.onPause(); } @Override protected void onDestroy() { if(mAudioRecord != null) { mAudioRecord.release(); } super.onDestroy(); } public OnRecordPositionUpdateListener mListener = new OnRecordPositionUpdateListener() { public void onPeriodicNotification(AudioRecord recorder) { } public void onMarkerReached(AudioRecord recorder) { inRecordMode = false; } }; private void initAudioRecord() { try { int sampleRate = 8000; int channelConfig = AudioFormat.CHANNEL_IN_MONO; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; mAudioBufferSize = 2 * AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); mAudioBufferSampleSize = mAudioBufferSize / 2; mAudioRecord = new AudioRecord( MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, mAudioBufferSize); } catch (IllegalArgumentException e) { e.printStackTrace(); } mAudioRecord.setNotificationMarkerPosition(10000); mAudioRecord.setPositionNotificationPeriod(1000); mAudioRecord.setRecordPositionUpdateListener(mListener); int audioRecordState = mAudioRecord.getState(); if(audioRecordState != AudioRecord.STATE_INITIALIZED) { finish(); } } private void getSamples() { if(mAudioRecord == null) return; short[] audioBuffer = new short[mAudioBufferSampleSize]; mAudioRecord.startRecording(); int audioRecordingState = mAudioRecord.getRecordingState(); if(audioRecordingState != AudioRecord.RECORDSTATE_RECORDING) { finish(); } while(inRecordMode) { int samplesRead = mAudioRecord.read(audioBuffer, 0, mAudioBufferSampleSize); } mAudioRecord.stop(); } }
Using BackgroundAudioServiceBinder
package app.test; import android.app.Activity; import android.app.Service; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; class BackgroundAudioService extends Service implements OnCompletionListener { MediaPlayer mediaPlayer; public class BackgroundAudioServiceBinder extends Binder { BackgroundAudioService getService() { return BackgroundAudioService.this; } } private final IBinder basBinder = new BackgroundAudioServiceBinder(); @Override public IBinder onBind(Intent intent) { return basBinder; } public void haveFun() { if (mediaPlayer.isPlaying()) { mediaPlayer.seekTo(mediaPlayer.getCurrentPosition() - 2500); } } @Override public void onCreate() { mediaPlayer = MediaPlayer.create(this, R.raw.test);// raw/test.mp3 mediaPlayer.setOnCompletionListener(this); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.v("PLAYERSERVICE", "onStartCommand"); if (!mediaPlayer.isPlaying()) { mediaPlayer.start(); } return START_STICKY; } public void onDestroy() { if (mediaPlayer.isPlaying()) { mediaPlayer.stop(); } mediaPlayer.release(); Log.v("SIMPLESERVICE", "onDestroy"); } public void onCompletion(MediaPlayer _mediaPlayer) { stopSelf(); } } public class Test extends Activity implements OnClickListener { private BackgroundAudioService baService; Button startPlaybackButton, stopPlaybackButton, haveFunButton; Intent playbackServiceIntent; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startPlaybackButton = (Button) this .findViewById(R.id.StartPlaybackButton); stopPlaybackButton = (Button) this .findViewById(R.id.StopPlaybackButton); haveFunButton = (Button) this.findViewById(R.id.HaveFunButton); startPlaybackButton.setOnClickListener(this); stopPlaybackButton.setOnClickListener(this); haveFunButton.setOnClickListener(this); playbackServiceIntent = new Intent(this, BackgroundAudioService.class); } public void onClick(View v) { if (v == startPlaybackButton) { startService(playbackServiceIntent); bindService(playbackServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE); } else if (v == stopPlaybackButton) { unbindService(serviceConnection); stopService(playbackServiceIntent); } else if (v == haveFunButton) { baService.haveFun(); } } private ServiceConnection serviceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder baBinder) { baService = ((BackgroundAudioService.BackgroundAudioServiceBinder) baBinder) .getService(); } public void onServiceDisconnected(ComponentName className) { baService = null; } }; } //main.xml <?xml version="1.0" encoding="utf-8"?> <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="Background Audio Player" /> <Button android:text="Start Playback" android:id="@+id/StartPlaybackButton" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Stop Playback" android:id="@+id/StopPlaybackButton" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Have Fun" android:id="@+id/HaveFunButton" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> </LinearLayout>
Custom Audio Recorder
package app.test; import java.io.File; import java.io.IOException; import android.app.Activity; import android.media.MediaPlayer; import android.media.MediaRecorder; import android.media.MediaPlayer.OnCompletionListener; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class Test extends Activity implements OnClickListener, OnCompletionListener { TextView statusTextView; Button startRecording, stopRecording, playRecording, finishButton; MediaRecorder recorder; MediaPlayer player; File audioFile; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); statusTextView = (TextView) this.findViewById(R.id.StatusTextView); statusTextView.setText("Ready"); stopRecording = (Button) this.findViewById(R.id.StopRecording); startRecording = (Button) this.findViewById(R.id.StartRecording); playRecording = (Button) this.findViewById(R.id.PlayRecording); finishButton = (Button) this.findViewById(R.id.FinishButton); startRecording.setOnClickListener(this); stopRecording.setOnClickListener(this); playRecording.setOnClickListener(this); finishButton.setOnClickListener(this); stopRecording.setEnabled(false); playRecording.setEnabled(false); } public void onClick(View v) { if (v == finishButton) { finish(); } else if (v == stopRecording) { recorder.stop(); recorder.release(); player = new MediaPlayer(); player.setOnCompletionListener(this); try { player.setDataSource(audioFile.getAbsolutePath()); player.prepare(); } catch (Exception e) { throw new RuntimeException( "Exception in MediaPlayer.prepare", e); } statusTextView.setText("Ready to Play"); playRecording.setEnabled(true); stopRecording.setEnabled(false); startRecording.setEnabled(true); } else if (v == startRecording) { recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+ "/files/"); path.mkdirs(); try { audioFile = File.createTempFile("recording", ".3gp", path); recorder.setOutputFile(audioFile.getAbsolutePath()); } catch (Exception e) { throw new RuntimeException( "Exception on MediaRecorder.prepare", e); } recorder.start(); statusTextView.setText("Recording"); playRecording.setEnabled(false); stopRecording.setEnabled(true); startRecording.setEnabled(false); } else if (v == playRecording) { player.start(); statusTextView.setText("Playing"); playRecording.setEnabled(false); stopRecording.setEnabled(false); startRecording.setEnabled(false); } } public void onCompletion(MediaPlayer mp) { playRecording.setEnabled(true); stopRecording.setEnabled(false); startRecording.setEnabled(true); statusTextView.setText("Ready"); } } //main.xml <?xml version="1.0" encoding="utf-8"?> <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="wrap_content" android:layout_height="wrap_content" android:id="@+id/StatusTextView" android:text="Status" android:textSize="35dip"></TextView> <Button android:text="Start Recording" android:id="@+id/StartRecording" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Stop Recording" android:id="@+id/StopRecording" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Play Recording" android:id="@+id/PlayRecording" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/FinishButton" android:text="Finish"></Button> </LinearLayout>
Set Audio source and format
package app.test; import java.io.File; import android.app.Activity; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaRecorder; import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class Test extends Activity implements OnClickListener, OnCompletionListener { TextView statusTextView, amplitudeTextView; Button startRecording, stopRecording, playRecording, finishButton; MediaRecorder recorder; MediaPlayer player; File audioFile; RecordAmplitude recordAmplitude; boolean isRecording = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); statusTextView = (TextView) this.findViewById(R.id.StatusTextView); statusTextView.setText("Ready"); amplitudeTextView = (TextView) this.findViewById(R.id.AmplitudeTextView); amplitudeTextView.setText("0"); stopRecording = (Button) this.findViewById(R.id.StopRecording); startRecording = (Button) this.findViewById(R.id.StartRecording); playRecording = (Button) this.findViewById(R.id.PlayRecording); finishButton = (Button) this.findViewById(R.id.FinishButton); startRecording.setOnClickListener(this); stopRecording.setOnClickListener(this); playRecording.setOnClickListener(this); finishButton.setOnClickListener(this); stopRecording.setEnabled(false); playRecording.setEnabled(false); } public void onClick(View v) { if (v == finishButton) { finish(); } else if (v == stopRecording) { isRecording = false; recordAmplitude.cancel(true); recorder.stop(); recorder.release(); player = new MediaPlayer(); player.setOnCompletionListener(this); try { player.setDataSource(audioFile.getAbsolutePath()); player.prepare(); } catch (Exception e) { throw new RuntimeException("IOException in MediaPlayer.prepare", e); } statusTextView.setText("Ready to Play"); playRecording.setEnabled(true); stopRecording.setEnabled(false); startRecording.setEnabled(true); } else if (v == startRecording) { recorder = new MediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); File path = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+ "/files/"); path.mkdirs(); try { audioFile = File.createTempFile("recording", ".3gp", path); recorder.setOutputFile(audioFile.getAbsolutePath()); recorder.prepare(); } catch (Exception e) { throw new RuntimeException( "IOException on MediaRecorder.prepare", e); } recorder.start(); isRecording = true; recordAmplitude = new RecordAmplitude(); recordAmplitude.execute(); statusTextView.setText("Recording"); playRecording.setEnabled(false); stopRecording.setEnabled(true); startRecording.setEnabled(false); } else if (v == playRecording) { player.start(); statusTextView.setText("Playing"); playRecording.setEnabled(false); stopRecording.setEnabled(false); startRecording.setEnabled(false); } } public void onCompletion(MediaPlayer mp) { playRecording.setEnabled(true); stopRecording.setEnabled(false); startRecording.setEnabled(true); statusTextView.setText("Ready"); } private class RecordAmplitude extends AsyncTask<Void, Integer, Void> { @Override protected Void doInBackground(Void... params) { while (isRecording) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } publishProgress(recorder.getMaxAmplitude()); } return null; } protected void onProgressUpdate(Integer... progress) { amplitudeTextView.setText(progress[0].toString()); } } } //main.xml <?xml version="1.0" encoding="utf-8"?> <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="wrap_content" android:layout_height="wrap_content" android:id="@+id/StatusTextView" android:text="Status" android:textSize="35dip"></TextView> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/AmplitudeTextView" android:textSize="35dip" android:text="0"></TextView> <Button android:text="Start Recording" android:id="@+id/StartRecording" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Stop Recording" android:id="@+id/StopRecording" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:text="Play Recording" android:id="@+id/PlayRecording" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/FinishButton" android:text="Finish"></Button> </LinearLayout>
Audio processing library
//GNU General Public License, version 2 package ca.uol.aig.fftpack; /** * Construct a 1-D complex data sequence. */ public class Complex1D { /** * <em>x</em>[<em>i</em>] is the real part of <em>i</em>-th complex data. */ public double x[]; /** * <em>y</em>[<em>i</em>] is the imaginary part of <em>i</em>-th complex data. */ public double y[]; } package ca.uol.aig.fftpack; /** * @author Baoshe Zhang * @author Astronomical Instrument Group of University of Lethbridge. */ class RealDoubleFFT_Mixed { // ******************************************************************** // // Real-Valued FFT Initialization. // ******************************************************************** // /** * Initialization of Real FFT. */ void rffti(int n, double wtable[]) /* length of wtable = 2*n + 15 */ { if (n == 1) return; rffti1(n, wtable, 0); } /*--------------------------------------------------------- rffti1: further initialization of Real FFT --------------------------------------------------------*/ void rffti1(int n, double wtable[], int offset) { double argh; int ntry=0, i, j; double argld; int k1, l1, l2, ib; double fi; int ld, ii, nf, ip, nl, is, nq, nr; double arg; int ido, ipm; int nfm1; // Create a working array. tempData = new double[n]; nl=n; nf=0; j=0; factorize_loop: while(true) { ++j; if(j<=4) ntry=NTRY_H[j-1]; else ntry+=2; do { nq=nl / ntry; nr=nl-ntry*nq; if(nr !=0) continue factorize_loop; ++nf; wtable[nf+1+2*n+offset]=ntry; nl=nq; if(ntry==2 && nf !=1) { for(i=2; i<=nf; i++) { ib=nf-i+2; wtable[ib+1+2*n+offset]=wtable[ib+2*n+offset]; } wtable[2+2*n+offset]=2; } }while(nl !=1); break factorize_loop; } wtable[0+2*n+offset] = n; wtable[1+2*n+offset] = nf; argh=TWO_PI /(double)(n); is=0; nfm1=nf-1; l1=1; if(nfm1==0) return; for(k1=1; k1<=nfm1; k1++) { ip=(int)wtable[k1+1+2*n+offset]; ld=0; l2=l1*ip; ido=n / l2; ipm=ip-1; for(j=1; j<=ipm;++j) { ld+=l1; i=is; argld=(double)ld*argh; fi=0; for(ii=3; ii<=ido; ii+=2) { i+=2; fi+=1; arg=fi*argld; wtable[i-2+n+offset] = Math.cos(arg); wtable[i-1+n+offset] = Math.sin(arg); } is+=ido; } l1=l2; } } /*rffti1*/ // ******************************************************************** // // Real-Valued FFT -- Forward Transform. // ******************************************************************** // /*--------------------------------------------------------- rfftf: Real forward FFT --------------------------------------------------------*/ void rfftf(int n, double r[], double wtable[]) { if(n==1) return; rfftf1(n, r, wtable, 0); } /*rfftf*/ /*--------------------------------------------------------- rfftf1: further processing of Real forward FFT --------------------------------------------------------*/ void rfftf1(int n, double[] c, final double[] wtable, int offset) { final double[] td = tempData; System.arraycopy(wtable, offset, td, 0, n); int nf = (int) wtable[1 + 2 * n + offset]; int na = 1; int l2 = n; int iw = n - 1 + n + offset; for (int k1 = 1; k1 <= nf; ++k1) { int kh = nf - k1; int ip = (int) wtable[kh + 2 + 2 * n + offset]; int l1 = l2 / ip; int ido = n / l2; int idl1 = ido * l1; iw -= (ip - 1) * ido; na = 1 - na; if (ip == 4) { if (na == 0) radf4(ido, l1, c, td, wtable, iw); else radf4(ido, l1, td, c, wtable, iw); } else if (ip == 2) { if (na == 0) radf2(ido, l1, c, td, wtable, iw); else radf2(ido, l1, td, c, wtable, iw); } else if (ip == 3) { if (na == 0) radf3(ido, l1, c, td, wtable, iw); else radf3(ido, l1, td, c, wtable, iw); } else if (ip == 5) { if (na == 0) radf5(ido, l1, c, td, wtable, iw); else radf5(ido, l1, td, c, wtable, iw); } else { if (ido == 1) na = 1 - na; if (na == 0) { radfg(ido, ip, l1, idl1, c, c, c, td, td, wtable, iw); na = 1; } else { radfg(ido, ip, l1, idl1, td, td, td, c, c, wtable, iw); na = 0; } } l2 = l1; } // If na == 1, the results are in c. Otherwise they're in tempData. if (na == 0) for (int i = 0; i < n; i++) c[i] = td[i]; } // ******************************************************************** // // Real-Valued FFT -- Reverse Transform. // ******************************************************************** // /*--------------------------------------------------------- rfftb: Real backward FFT --------------------------------------------------------*/ void rfftb(int n, double r[], double wtable[]) { if(n==1) return; rfftb1(n, r, wtable, 0); } /*rfftb*/ /*--------------------------------------------------------- rfftb1: further processing of Real backward FFT --------------------------------------------------------*/ void rfftb1(int n, double c[], final double wtable[], int offset) { int k1, l1, l2, na, nf, ip, iw, ido, idl1; final double[] td = tempData; System.arraycopy(wtable, offset, td, 0, n); nf=(int)wtable[1+2*n+offset]; na=0; l1=1; iw=n+offset; for(k1=1; k1<=nf; k1++) { ip=(int)wtable[k1+1+2*n+offset]; l2=ip*l1; ido=n / l2; idl1=ido*l1; if(ip==4) { if(na==0) { radb4(ido, l1, c, td, wtable, iw); } else { radb4(ido, l1, td, c, wtable, iw); } na=1-na; } else if(ip==2) { if(na==0) { radb2(ido, l1, c, td, wtable, iw); } else { radb2(ido, l1, td, c, wtable, iw); } na=1-na; } else if(ip==3) { if(na==0) { radb3(ido, l1, c, td, wtable, iw); } else { radb3(ido, l1, td, c, wtable, iw); } na=1-na; } else if(ip==5) { if(na==0) { radb5(ido, l1, c, td, wtable, iw); } else { radb5(ido, l1, td, c, wtable, iw); } na=1-na; } else { if(na==0) { radbg(ido, ip, l1, idl1, c, c, c, td, td, wtable, iw); } else { radbg(ido, ip, l1, idl1, td, td, td, c, c, wtable, iw); } if(ido==1) na=1-na; } l1=l2; iw+=(ip-1)*ido; } if (na == 1) for (int i = 0; i < n; i++) c[i] = td[i]; } // ******************************************************************** // // Real-Valued FFT -- General Subroutines. // ******************************************************************** // /*--------------------------------------------------------- radfg: Real FFT's forward processing of general factor --------------------------------------------------------*/ private void radfg(int ido, int ip, int l1, int idl1, double cc[], double c1[], double c2[], double ch[], double ch2[], final double wtable[], int offset) { int idij, ipph, i, j, k, l, j2, ic, jc, lc, ik, is, nbd; double dc2, ai1, ai2, ar1, ar2, ds2, dcp, arg, dsp, ar1h, ar2h; int iw1 = offset; arg=TWO_PI / (double)ip; dcp=Math.cos(arg); dsp=Math.sin(arg); ipph=(ip+1)/ 2; nbd=(ido-1)/ 2; if(ido !=1) { for(ik=0; ik<idl1; ik++) ch2[ik]=c2[ik]; for(j=1; j<ip; j++) for(k=0; k<l1; k++) ch[(k+j*l1)*ido]=c1[(k+j*l1)*ido]; if(nbd<=l1) { is=-ido; for(j=1; j<ip; j++) { is+=ido; idij=is-1; for(i=2; i<ido; i+=2) { idij+=2; for(k=0; k<l1; k++) { ch[i-1+(k+j*l1)*ido]= wtable[idij-1+iw1]*c1[i-1+(k+j*l1)*ido] +wtable[idij+iw1]*c1[i+(k+j*l1)*ido]; ch[i+(k+j*l1)*ido]= wtable[idij-1+iw1]*c1[i+(k+j*l1)*ido] -wtable[idij+iw1]*c1[i-1+(k+j*l1)*ido]; } } } } else { is=-ido; for(j=1; j<ip; j++) { is+=ido; for(k=0; k<l1; k++) { idij=is-1; for(i=2; i<ido; i+=2) { idij+=2; ch[i-1+(k+j*l1)*ido]= wtable[idij-1+iw1]*c1[i-1+(k+j*l1)*ido] +wtable[idij+iw1]*c1[i+(k+j*l1)*ido]; ch[i+(k+j*l1)*ido]= wtable[idij-1+iw1]*c1[i+(k+j*l1)*ido] -wtable[idij+iw1]*c1[i-1+(k+j*l1)*ido]; } } } } if(nbd>=l1) { for(j=1; j<ipph; j++) { jc=ip-j; for(k=0; k<l1; k++) { for(i=2; i<ido; i+=2) { c1[i-1+(k+j*l1)*ido]=ch[i-1+(k+j*l1)*ido]+ch[i-1+(k+jc*l1)*ido]; c1[i-1+(k+jc*l1)*ido]=ch[i+(k+j*l1)*ido]-ch[i+(k+jc*l1)*ido]; c1[i+(k+j*l1)*ido]=ch[i+(k+j*l1)*ido]+ch[i+(k+jc*l1)*ido]; c1[i+(k+jc*l1)*ido]=ch[i-1+(k+jc*l1)*ido]-ch[i-1+(k+j*l1)*ido]; } } } } else { for(j=1; j<ipph; j++) { jc=ip-j; for(i=2; i<ido; i+=2) { for(k=0; k<l1; k++) { c1[i-1+(k+j*l1)*ido]= ch[i-1+(k+j*l1)*ido]+ch[i-1+(k+jc*l1)*ido]; c1[i-1+(k+jc*l1)*ido]=ch[i+(k+j*l1)*ido]-ch[i+(k+jc*l1)*ido]; c1[i+(k+j*l1)*ido]=ch[i+(k+j*l1)*ido]+ch[i+(k+jc*l1)*ido]; c1[i+(k+jc*l1)*ido]=ch[i-1+(k+jc*l1)*ido]-ch[i-1+(k+j*l1)*ido]; } } } } } else { for(ik=0; ik<idl1; ik++) c2[ik]=ch2[ik]; } for(j=1; j<ipph; j++) { jc=ip-j; for(k=0; k<l1; k++) { c1[(k+j*l1)*ido]=ch[(k+j*l1)*ido]+ch[(k+jc*l1)*ido]; c1[(k+jc*l1)*ido]=ch[(k+jc*l1)*ido]-ch[(k+j*l1)*ido]; } } ar1=1; ai1=0; for(l=1; l<ipph; l++) { lc=ip-l; ar1h=dcp*ar1-dsp*ai1; ai1=dcp*ai1+dsp*ar1; ar1=ar1h; for(ik=0; ik<idl1; ik++) { ch2[ik+l*idl1]=c2[ik]+ar1*c2[ik+idl1]; ch2[ik+lc*idl1]=ai1*c2[ik+(ip-1)*idl1]; } dc2=ar1; ds2=ai1; ar2=ar1; ai2=ai1; for(j=2; j<ipph; j++) { jc=ip-j; ar2h=dc2*ar2-ds2*ai2; ai2=dc2*ai2+ds2*ar2; ar2=ar2h; for(ik=0; ik<idl1; ik++) { ch2[ik+l*idl1]+=ar2*c2[ik+j*idl1]; ch2[ik+lc*idl1]+=ai2*c2[ik+jc*idl1]; } } } for(j=1; j<ipph; j++) for(ik=0; ik<idl1; ik++) ch2[ik]+=c2[ik+j*idl1]; if(ido>=l1) { for(k=0; k<l1; k++) { for(i=0; i<ido; i++) { cc[i+k*ip*ido]=ch[i+k*ido]; } } } else { for(i=0; i<ido; i++) { for(k=0; k<l1; k++) { cc[i+k*ip*ido]=ch[i+k*ido]; } } } for(j=1; j<ipph; j++) { jc=ip-j; j2=2*j; for(k=0; k<l1; k++) { cc[ido-1+(j2-1+k*ip)*ido]=ch[(k+j*l1)*ido]; cc[(j2+k*ip)*ido]=ch[(k+jc*l1)*ido]; } } if(ido==1) return; if(nbd>=l1) { for(j=1; j<ipph; j++) { jc=ip-j; j2=2*j; for(k=0; k<l1; k++) { for(i=2; i<ido; i+=2) { ic=ido-i; cc[i-1+(j2+k*ip)*ido]=ch[i-1+(k+j*l1)*ido]+ch[i-1+(k+jc*l1)*ido]; cc[ic-1+(j2-1+k*ip)*ido]=ch[i-1+(k+j*l1)*ido]-ch[i-1+(k+jc*l1)*ido]; cc[i+(j2+k*ip)*ido]=ch[i+(k+j*l1)*ido]+ch[i+(k+jc*l1)*ido]; cc[ic+(j2-1+k*ip)*ido]=ch[i+(k+jc*l1)*ido]-ch[i+(k+j*l1)*ido]; } } } } else { for(j=1; j<ipph; j++) { jc=ip-j; j2=2*j; for(i=2; i<ido; i+=2) { ic=ido-i; for(k=0; k<l1; k++) { cc[i-1+(j2+k*ip)*ido]=ch[i-1+(k+j*l1)*ido]+ch[i-1+(k+jc*l1)*ido]; cc[ic-1+(j2-1+k*ip)*ido]=ch[i-1+(k+j*l1)*ido]-ch[i-1+(k+jc*l1)*ido]; cc[i+(j2+k*ip)*ido]=ch[i+(k+j*l1)*ido]+ch[i+(k+jc*l1)*ido]; cc[ic+(j2-1+k*ip)*ido]=ch[i+(k+jc*l1)*ido]-ch[i+(k+j*l1)*ido]; } } } } } /*--------------------------------------------------------- radbg: Real FFT's backward processing of general factor --------------------------------------------------------*/ private void radbg(int ido, int ip, int l1, int idl1, double cc[], double c1[], double c2[], double ch[], double ch2[], final double wtable[], int offset) { int idij, ipph, i, j, k, l, j2, ic, jc, lc, ik, is; double dc2, ai1, ai2, ar1, ar2, ds2; int nbd; double dcp, arg, dsp, ar1h, ar2h; int iw1 = offset; arg=TWO_PI / (double)ip; dcp=Math.cos(arg); dsp=Math.sin(arg); nbd=(ido-1)/ 2; ipph=(ip+1)/ 2; if(ido>=l1) { for(k=0; k<l1; k++) { for(i=0; i<ido; i++) { ch[i+k*ido]=cc[i+k*ip*ido]; } } } else { for(i=0; i<ido; i++) { for(k=0; k<l1; k++) { ch[i+k*ido]=cc[i+k*ip*ido]; } } } for(j=1; j<ipph; j++) { jc=ip-j; j2=2*j; for(k=0; k<l1; k++) { ch[(k+j*l1)*ido]=cc[ido-1+(j2-1+k*ip)*ido]+cc[ido-1+(j2-1+k*ip)*ido]; ch[(k+jc*l1)*ido]=cc[(j2+k*ip)*ido]+cc[(j2+k*ip)*ido]; } } if(ido !=1) { if(nbd>=l1) { for(j=1; j<ipph; j++) { jc=ip-j; for(k=0; k<l1; k++) { for(i=2; i<ido; i+=2) { ic=ido-i; ch[i-1+(k+j*l1)*ido]=cc[i-1+(2*j+k*ip)*ido]+cc[ic-1+(2*j-1+k*ip)*ido]; ch[i-1+(k+jc*l1)*ido]=cc[i-1+(2*j+k*ip)*ido]-cc[ic-1+(2*j-1+k*ip)*ido]; ch[i+(k+j*l1)*ido]=cc[i+(2*j+k*ip)*ido]-cc[ic+(2*j-1+k*ip)*ido]; ch[i+(k+jc*l1)*ido]=cc[i+(2*j+k*ip)*ido]+cc[ic+(2*j-1+k*ip)*ido]; } } } } else { for(j=1; j<ipph; j++) { jc=ip-j; for(i=2; i<ido; i+=2) { ic=ido-i; for(k=0; k<l1; k++) { ch[i-1+(k+j*l1)*ido]=cc[i-1+(2*j+k*ip)*ido]+cc[ic-1+(2*j-1+k*ip)*ido]; ch[i-1+(k+jc*l1)*ido]=cc[i-1+(2*j+k*ip)*ido]-cc[ic-1+(2*j-1+k*ip)*ido]; ch[i+(k+j*l1)*ido]=cc[i+(2*j+k*ip)*ido]-cc[ic+(2*j-1+k*ip)*ido]; ch[i+(k+jc*l1)*ido]=cc[i+(2*j+k*ip)*ido]+cc[ic+(2*j-1+k*ip)*ido]; } } } } } ar1=1; ai1=0; for(l=1; l<ipph; l++) { lc=ip-l; ar1h=dcp*ar1-dsp*ai1; ai1=dcp*ai1+dsp*ar1; ar1=ar1h; for(ik=0; ik<idl1; ik++) { c2[ik+l*idl1]=ch2[ik]+ar1*ch2[ik+idl1]; c2[ik+lc*idl1]=ai1*ch2[ik+(ip-1)*idl1]; } dc2=ar1; ds2=ai1; ar2=ar1; ai2=ai1; for(j=2; j<ipph; j++) { jc=ip-j; ar2h=dc2*ar2-ds2*ai2; ai2=dc2*ai2+ds2*ar2; ar2=ar2h; for(ik=0; ik<idl1; ik++) { c2[ik+l*idl1]+=ar2*ch2[ik+j*idl1]; c2[ik+lc*idl1]+=ai2*ch2[ik+jc*idl1]; } } } for(j=1; j<ipph; j++) { for(ik=0; ik<idl1; ik++) { ch2[ik]+=ch2[ik+j*idl1]; } } for(j=1; j<ipph; j++) { jc=ip-j; for(k=0; k<l1; k++) { ch[(k+j*l1)*ido]=c1[(k+j*l1)*ido]-c1[(k+jc*l1)*ido]; ch[(k+jc*l1)*ido]=c1[(k+j*l1)*ido]+c1[(k+jc*l1)*ido]; } } if(ido==1) return; if(nbd>=l1) { for(j=1; j<ipph; j++) { jc=ip-j; for(k=0; k<l1; k++) { for(i=2; i<ido; i+=2) { ch[i-1+(k+j*l1)*ido]=c1[i-1+(k+j*l1)*ido]-c1[i+(k+jc*l1)*ido]; ch[i-1+(k+jc*l1)*ido]=c1[i-1+(k+j*l1)*ido]+c1[i+(k+jc*l1)*ido]; ch[i+(k+j*l1)*ido]=c1[i+(k+j*l1)*ido]+c1[i-1+(k+jc*l1)*ido]; ch[i+(k+jc*l1)*ido]=c1[i+(k+j*l1)*ido]-c1[i-1+(k+jc*l1)*ido]; } } } } else { for(j=1; j<ipph; j++) { jc=ip-j; for(i=2; i<ido; i+=2) { for(k=0; k<l1; k++) { ch[i-1+(k+j*l1)*ido]=c1[i-1+(k+j*l1)*ido]-c1[i+(k+jc*l1)*ido]; ch[i-1+(k+jc*l1)*ido]=c1[i-1+(k+j*l1)*ido]+c1[i+(k+jc*l1)*ido]; ch[i+(k+j*l1)*ido]=c1[i+(k+j*l1)*ido]+c1[i-1+(k+jc*l1)*ido]; ch[i+(k+jc*l1)*ido]=c1[i+(k+j*l1)*ido]-c1[i-1+(k+jc*l1)*ido]; } } } } for(ik=0; ik<idl1; ik++) c2[ik]=ch2[ik]; for(j=1; j<ip; j++) for(k=0; k<l1; k++) c1[(k+j*l1)*ido]=ch[(k+j*l1)*ido]; if(nbd<=l1) { is=-ido; for(j=1; j<ip; j++) { is+=ido; idij=is-1; for(i=2; i<ido; i+=2) { idij+=2; for(k=0; k<l1; k++) { c1[i-1+(k+j*l1)*ido] = wtable[idij-1+iw1]*ch[i-1+(k+j*l1)*ido] -wtable[idij+iw1]*ch[i+(k+j*l1)*ido]; c1[i+(k+j*l1)*ido] = wtable[idij-1+iw1]*ch[i+(k+j*l1)*ido] +wtable[idij+iw1]*ch[i-1+(k+j*l1)*ido]; } } } } else { is=-ido; for(j=1; j<ip; j++) { is+=ido; for(k=0; k<l1; k++) { idij=is-1; for(i=2; i<ido; i+=2) { idij+=2; c1[i-1+(k+j*l1)*ido] = wtable[idij-1+iw1]*ch[i-1+(k+j*l1)*ido] -wtable[idij+iw1]*ch[i+(k+j*l1)*ido]; c1[i+(k+j*l1)*ido] = wtable[idij-1+iw1]*ch[i+(k+j*l1)*ido] +wtable[idij+iw1]*ch[i-1+(k+j*l1)*ido]; } } } } } // ******************************************************************** // // Real-Valued FFT -- Factor-Specific Optimized Subroutines. // ******************************************************************** // /*------------------------------------------------- radf2: Real FFT's forward processing of factor 2 -------------------------------------------------*/ private void radf2(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset) { int i, k, ic; double ti2, tr2; int iw1; iw1 = offset; for(k=0; k<l1; k++) { ch[2*k*ido]=cc[k*ido]+cc[(k+l1)*ido]; ch[(2*k+1)*ido+ido-1]=cc[k*ido]-cc[(k+l1)*ido]; } if(ido<2) return; if(ido !=2) { for(k=0; k<l1; k++) { for(i=2; i<ido; i+=2) { ic=ido-i; tr2 = wtable[i-2+iw1]*cc[i-1+(k+l1)*ido] +wtable[i-1+iw1]*cc[i+(k+l1)*ido]; ti2 = wtable[i-2+iw1]*cc[i+(k+l1)*ido] -wtable[i-1+iw1]*cc[i-1+(k+l1)*ido]; ch[i+2*k*ido]=cc[i+k*ido]+ti2; ch[ic+(2*k+1)*ido]=ti2-cc[i+k*ido]; ch[i-1+2*k*ido]=cc[i-1+k*ido]+tr2; ch[ic-1+(2*k+1)*ido]=cc[i-1+k*ido]-tr2; } } if(ido%2==1)return; } for(k=0; k<l1; k++) { ch[(2*k+1)*ido]=-cc[ido-1+(k+l1)*ido]; ch[ido-1+2*k*ido]=cc[ido-1+k*ido]; } } /*------------------------------------------------- radb2: Real FFT's backward processing of factor 2 -------------------------------------------------*/ private void radb2(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset) { int i, k, ic; double ti2, tr2; int iw1 = offset; for(k=0; k<l1; k++) { ch[k*ido]=cc[2*k*ido]+cc[ido-1+(2*k+1)*ido]; ch[(k+l1)*ido]=cc[2*k*ido]-cc[ido-1+(2*k+1)*ido]; } if(ido<2) return; if(ido !=2) { for(k=0; k<l1;++k) { for(i=2; i<ido; i+=2) { ic=ido-i; ch[i-1+k*ido]=cc[i-1+2*k*ido]+cc[ic-1+(2*k+1)*ido]; tr2=cc[i-1+2*k*ido]-cc[ic-1+(2*k+1)*ido]; ch[i+k*ido]=cc[i+2*k*ido]-cc[ic+(2*k+1)*ido]; ti2=cc[i+(2*k)*ido]+cc[ic+(2*k+1)*ido]; ch[i-1+(k+l1)*ido]=wtable[i-2+iw1]*tr2-wtable[i-1+iw1]*ti2; ch[i+(k+l1)*ido]=wtable[i-2+iw1]*ti2+wtable[i-1+iw1]*tr2; } } if(ido%2==1) return; } for(k=0; k<l1; k++) { ch[ido-1+k*ido]=2*cc[ido-1+2*k*ido]; ch[ido-1+(k+l1)*ido]=-2*cc[(2*k+1)*ido]; } } /*------------------------------------------------- radf3: Real FFT's forward processing of factor 3 -------------------------------------------------*/ private void radf3(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset) { int i, k, ic; double ci2, di2, di3, cr2, dr2, dr3, ti2, ti3, tr2, tr3; int iw1, iw2; iw1 = offset; iw2 = iw1 + ido; for(k=0; k<l1; k++) { cr2=cc[(k+l1)*ido]+cc[(k+2*l1)*ido]; ch[3*k*ido]=cc[k*ido]+cr2; ch[(3*k+2)*ido]=TAU_I*(cc[(k+l1*2)*ido]-cc[(k+l1)*ido]); ch[ido-1+(3*k+1)*ido]=cc[k*ido]+TAU_R*cr2; } if(ido==1) return; for(k=0; k<l1; k++) { for(i=2; i<ido; i+=2) { ic=ido-i; dr2 = wtable[i-2+iw1]*cc[i-1+(k+l1)*ido] +wtable[i-1+iw1]*cc[i+(k+l1)*ido]; di2 = wtable[i-2+iw1]*cc[i+(k+l1)*ido] -wtable[i-1+iw1]*cc[i-1+(k+l1)*ido]; dr3 = wtable[i-2+iw2]*cc[i-1+(k+l1*2)*ido] +wtable[i-1+iw2]*cc[i+(k+l1*2)*ido]; di3 = wtable[i-2+iw2]*cc[i+(k+l1*2)*ido] -wtable[i-1+iw2]*cc[i-1+(k+l1*2)*ido]; cr2 = dr2+dr3; ci2 = di2+di3; ch[i-1+3*k*ido]=cc[i-1+k*ido]+cr2; ch[i+3*k*ido]=cc[i+k*ido]+ci2; tr2=cc[i-1+k*ido]+TAU_R*cr2; ti2=cc[i+k*ido]+TAU_R*ci2; tr3=TAU_I*(di2-di3); ti3=TAU_I*(dr3-dr2); ch[i-1+(3*k+2)*ido]=tr2+tr3; ch[ic-1+(3*k+1)*ido]=tr2-tr3; ch[i+(3*k+2)*ido]=ti2+ti3; ch[ic+(3*k+1)*ido]=ti3-ti2; } } } /*------------------------------------------------- radb3: Real FFT's backward processing of factor 3 -------------------------------------------------*/ private void radb3(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset) { int i, k, ic; double ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2; int iw1, iw2; iw1 = offset; iw2 = iw1 + ido; for(k=0; k<l1; k++) { tr2=2*cc[ido-1+(3*k+1)*ido]; cr2=cc[3*k*ido]+TAU_R*tr2; ch[k*ido]=cc[3*k*ido]+tr2; ci3=2*TAU_I*cc[(3*k+2)*ido]; ch[(k+l1)*ido]=cr2-ci3; ch[(k+2*l1)*ido]=cr2+ci3; } if(ido==1) return; for(k=0; k<l1; k++) { for(i=2; i<ido; i+=2) { ic=ido-i; tr2=cc[i-1+(3*k+2)*ido]+cc[ic-1+(3*k+1)*ido]; cr2=cc[i-1+3*k*ido]+TAU_R*tr2; ch[i-1+k*ido]=cc[i-1+3*k*ido]+tr2; ti2=cc[i+(3*k+2)*ido]-cc[ic+(3*k+1)*ido]; ci2=cc[i+3*k*ido]+TAU_R*ti2; ch[i+k*ido]=cc[i+3*k*ido]+ti2; cr3=TAU_I*(cc[i-1+(3*k+2)*ido]-cc[ic-1+(3*k+1)*ido]); ci3=TAU_I*(cc[i+(3*k+2)*ido]+cc[ic+(3*k+1)*ido]); dr2=cr2-ci3; dr3=cr2+ci3; di2=ci2+cr3; di3=ci2-cr3; ch[i-1+(k+l1)*ido] = wtable[i-2+iw1]*dr2 -wtable[i-1+iw1]*di2; ch[i+(k+l1)*ido] = wtable[i-2+iw1]*di2 +wtable[i-1+iw1]*dr2; ch[i-1+(k+2*l1)*ido] = wtable[i-2+iw2]*dr3 -wtable[i-1+iw2]*di3; ch[i+(k+2*l1)*ido] = wtable[i-2+iw2]*di3 +wtable[i-1+iw2]*dr3; } } } /*------------------------------------------------- radf4: Real FFT's forward processing of factor 4 -------------------------------------------------*/ private void radf4(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset) { final double hsqt2=0.7071067811865475D; int i, k, ic; double ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; int iw1, iw2, iw3; iw1 = offset; iw2 = offset + ido; iw3 = iw2 + ido; for(k=0; k<l1; k++) { tr1=cc[(k+l1)*ido]+cc[(k+3*l1)*ido]; tr2=cc[k*ido]+cc[(k+2*l1)*ido]; ch[4*k*ido]=tr1+tr2; ch[ido-1+(4*k+3)*ido]=tr2-tr1; ch[ido-1+(4*k+1)*ido]=cc[k*ido]-cc[(k+2*l1)*ido]; ch[(4*k+2)*ido]=cc[(k+3*l1)*ido]-cc[(k+l1)*ido]; } if(ido<2) return; if(ido !=2) { for(k=0; k<l1; k++) { for(i=2; i<ido; i+=2) { ic=ido-i; cr2 = wtable[i-2+iw1]*cc[i-1+(k+l1)*ido] +wtable[i-1+iw1]*cc[i+(k+l1)*ido]; ci2 = wtable[i-2+iw1]*cc[i+(k+l1)*ido] -wtable[i-1+iw1]*cc[i-1+(k+l1)*ido]; cr3 = wtable[i-2+iw2]*cc[i-1+(k+2*l1)*ido] +wtable[i-1+iw2]*cc[i+(k+2*l1)*ido]; ci3 = wtable[i-2+iw2]*cc[i+(k+2*l1)*ido] -wtable[i-1+iw2]*cc[i-1+(k+2*l1)*ido]; cr4 = wtable[i-2+iw3]*cc[i-1+(k+3*l1)*ido] +wtable[i-1+iw3]*cc[i+(k+3*l1)*ido]; ci4 = wtable[i-2+iw3]*cc[i+(k+3*l1)*ido] -wtable[i-1+iw3]*cc[i-1+(k+3*l1)*ido]; tr1=cr2+cr4; tr4=cr4-cr2; ti1=ci2+ci4; ti4=ci2-ci4; ti2=cc[i+k*ido]+ci3; ti3=cc[i+k*ido]-ci3; tr2=cc[i-1+k*ido]+cr3; tr3=cc[i-1+k*ido]-cr3; ch[i-1+4*k*ido]=tr1+tr2; ch[ic-1+(4*k+3)*ido]=tr2-tr1; ch[i+4*k*ido]=ti1+ti2; ch[ic+(4*k+3)*ido]=ti1-ti2; ch[i-1+(4*k+2)*ido]=ti4+tr3; ch[ic-1+(4*k+1)*ido]=tr3-ti4; ch[i+(4*k+2)*ido]=tr4+ti3; ch[ic+(4*k+1)*ido]=tr4-ti3; } } if(ido%2==1) return; } for(k=0; k<l1; k++) { ti1=-hsqt2*(cc[ido-1+(k+l1)*ido]+cc[ido-1+(k+3*l1)*ido]); tr1=hsqt2*(cc[ido-1+(k+l1)*ido]-cc[ido-1+(k+3*l1)*ido]); ch[ido-1+4*k*ido]=tr1+cc[ido-1+k*ido]; ch[ido-1+(4*k+2)*ido]=cc[ido-1+k*ido]-tr1; ch[(4*k+1)*ido]=ti1-cc[ido-1+(k+2*l1)*ido]; ch[(4*k+3)*ido]=ti1+cc[ido-1+(k+2*l1)*ido]; } } /*------------------------------------------------- radb4: Real FFT's backward processing of factor 4 -------------------------------------------------*/ private void radb4(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset) { int i, k, ic; double ci2, ci3, ci4, cr2, cr3, cr4; double ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; int iw1, iw2, iw3; iw1 = offset; iw2 = iw1 + ido; iw3 = iw2 + ido; for(k=0; k<l1; k++) { tr1=cc[4*k*ido]-cc[ido-1+(4*k+3)*ido]; tr2=cc[4*k*ido]+cc[ido-1+(4*k+3)*ido]; tr3=cc[ido-1+(4*k+1)*ido]+cc[ido-1+(4*k+1)*ido]; tr4=cc[(4*k+2)*ido]+cc[(4*k+2)*ido]; ch[k*ido]=tr2+tr3; ch[(k+l1)*ido]=tr1-tr4; ch[(k+2*l1)*ido]=tr2-tr3; ch[(k+3*l1)*ido]=tr1+tr4; } if(ido<2) return; if(ido !=2) { for(k=0; k<l1;++k) { for(i=2; i<ido; i+=2) { ic=ido-i; ti1=cc[i+4*k*ido]+cc[ic+(4*k+3)*ido]; ti2=cc[i+4*k*ido]-cc[ic+(4*k+3)*ido]; ti3=cc[i+(4*k+2)*ido]-cc[ic+(4*k+1)*ido]; tr4=cc[i+(4*k+2)*ido]+cc[ic+(4*k+1)*ido]; tr1=cc[i-1+4*k*ido]-cc[ic-1+(4*k+3)*ido]; tr2=cc[i-1+4*k*ido]+cc[ic-1+(4*k+3)*ido]; ti4=cc[i-1+(4*k+2)*ido]-cc[ic-1+(4*k+1)*ido]; tr3=cc[i-1+(4*k+2)*ido]+cc[ic-1+(4*k+1)*ido]; ch[i-1+k*ido]=tr2+tr3; cr3=tr2-tr3; ch[i+k*ido]=ti2+ti3; ci3=ti2-ti3; cr2=tr1-tr4; cr4=tr1+tr4; ci2=ti1+ti4; ci4=ti1-ti4; ch[i-1+(k+l1)*ido] = wtable[i-2+iw1]*cr2 -wtable[i-1+iw1]*ci2; ch[i+(k+l1)*ido] = wtable[i-2+iw1]*ci2 +wtable[i-1+iw1]*cr2; ch[i-1+(k+2*l1)*ido] = wtable[i-2+iw2]*cr3 -wtable[i-1+iw2]*ci3; ch[i+(k+2*l1)*ido] = wtable[i-2+iw2]*ci3 +wtable[i-1+iw2]*cr3; ch[i-1+(k+3*l1)*ido] = wtable[i-2+iw3]*cr4 -wtable[i-1+iw3]*ci4; ch[i+(k+3*l1)*ido] = wtable[i-2+iw3]*ci4 +wtable[i-1+iw3]*cr4; } } if(ido%2==1) return; } for(k=0; k<l1; k++) { ti1=cc[(4*k+1)*ido]+cc[(4*k+3)*ido]; ti2=cc[(4*k+3)*ido]-cc[(4*k+1)*ido]; tr1=cc[ido-1+4*k*ido]-cc[ido-1+(4*k+2)*ido]; tr2=cc[ido-1+4*k*ido]+cc[ido-1+(4*k+2)*ido]; ch[ido-1+k*ido]=tr2+tr2; ch[ido-1+(k+l1)*ido]=SQRT_2*(tr1-ti1); ch[ido-1+(k+2*l1)*ido]=ti2+ti2; ch[ido-1+(k+3*l1)*ido]=-SQRT_2*(tr1+ti1); } } /*------------------------------------------------- radf5: Real FFT's forward processing of factor 5 -------------------------------------------------*/ private void radf5(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset) { final double tr11=0.309016994374947D; final double ti11=0.951056516295154D; final double tr12=-0.809016994374947D; final double ti12=0.587785252292473D; int i, k, ic; double ci2, di2, ci4, ci5, di3, di4, di5, ci3, cr2, cr3, dr2, dr3, dr4, dr5, cr5, cr4, ti2, ti3, ti5, ti4, tr2, tr3, tr4, tr5; int iw1, iw2, iw3, iw4; iw1 = offset; iw2 = iw1 + ido; iw3 = iw2 + ido; iw4 = iw3 + ido; for(k=0; k<l1; k++) { cr2=cc[(k+4*l1)*ido]+cc[(k+l1)*ido]; ci5=cc[(k+4*l1)*ido]-cc[(k+l1)*ido]; cr3=cc[(k+3*l1)*ido]+cc[(k+2*l1)*ido]; ci4=cc[(k+3*l1)*ido]-cc[(k+2*l1)*ido]; ch[5*k*ido]=cc[k*ido]+cr2+cr3; ch[ido-1+(5*k+1)*ido]=cc[k*ido]+tr11*cr2+tr12*cr3; ch[(5*k+2)*ido]=ti11*ci5+ti12*ci4; ch[ido-1+(5*k+3)*ido]=cc[k*ido]+tr12*cr2+tr11*cr3; ch[(5*k+4)*ido]=ti12*ci5-ti11*ci4; } if(ido==1) return; for(k=0; k<l1;++k) { for(i=2; i<ido; i+=2) { ic=ido-i; dr2 = wtable[i-2+iw1]*cc[i-1+(k+l1)*ido] +wtable[i-1+iw1]*cc[i+(k+l1)*ido]; di2 = wtable[i-2+iw1]*cc[i+(k+l1)*ido] -wtable[i-1+iw1]*cc[i-1+(k+l1)*ido]; dr3 = wtable[i-2+iw2]*cc[i-1+(k+2*l1)*ido] +wtable[i-1+iw2]*cc[i+(k+2*l1)*ido]; di3 = wtable[i-2+iw2]*cc[i+(k+2*l1)*ido] -wtable[i-1+iw2]*cc[i-1+(k+2*l1)*ido]; dr4 = wtable[i-2+iw3]*cc[i-1+(k+3*l1)*ido] +wtable[i-1+iw3]*cc[i+(k+3*l1)*ido]; di4 = wtable[i-2+iw3]*cc[i+(k+3*l1)*ido] -wtable[i-1+iw3]*cc[i-1+(k+3*l1)*ido]; dr5 = wtable[i-2+iw4]*cc[i-1+(k+4*l1)*ido] +wtable[i-1+iw4]*cc[i+(k+4*l1)*ido]; di5 = wtable[i-2+iw4]*cc[i+(k+4*l1)*ido] -wtable[i-1+iw4]*cc[i-1+(k+4*l1)*ido]; cr2=dr2+dr5; ci5=dr5-dr2; cr5=di2-di5; ci2=di2+di5; cr3=dr3+dr4; ci4=dr4-dr3; cr4=di3-di4; ci3=di3+di4; ch[i-1+5*k*ido]=cc[i-1+k*ido]+cr2+cr3; ch[i+5*k*ido]=cc[i+k*ido]+ci2+ci3; tr2=cc[i-1+k*ido]+tr11*cr2+tr12*cr3; ti2=cc[i+k*ido]+tr11*ci2+tr12*ci3; tr3=cc[i-1+k*ido]+tr12*cr2+tr11*cr3; ti3=cc[i+k*ido]+tr12*ci2+tr11*ci3; tr5=ti11*cr5+ti12*cr4; ti5=ti11*ci5+ti12*ci4; tr4=ti12*cr5-ti11*cr4; ti4=ti12*ci5-ti11*ci4; ch[i-1+(5*k+2)*ido]=tr2+tr5; ch[ic-1+(5*k+1)*ido]=tr2-tr5; ch[i+(5*k+2)*ido]=ti2+ti5; ch[ic+(5*k+1)*ido]=ti5-ti2; ch[i-1+(5*k+4)*ido]=tr3+tr4; ch[ic-1+(5*k+3)*ido]=tr3-tr4; ch[i+(5*k+4)*ido]=ti3+ti4; ch[ic+(5*k+3)*ido]=ti4-ti3; } } } /*------------------------------------------------- radb5: Real FFT's backward processing of factor 5 -------------------------------------------------*/ private void radb5(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset) { final double tr11=0.309016994374947D; final double ti11=0.951056516295154D; final double tr12=-0.809016994374947D; final double ti12=0.587785252292473D; int i, k, ic; double ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3, ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5; int iw1, iw2, iw3, iw4; iw1 = offset; iw2 = iw1 + ido; iw3 = iw2 + ido; iw4 = iw3 + ido; for(k=0; k<l1; k++) { ti5=2*cc[(5*k+2)*ido]; ti4=2*cc[(5*k+4)*ido]; tr2=2*cc[ido-1+(5*k+1)*ido]; tr3=2*cc[ido-1+(5*k+3)*ido]; ch[k*ido]=cc[5*k*ido]+tr2+tr3; cr2=cc[5*k*ido]+tr11*tr2+tr12*tr3; cr3=cc[5*k*ido]+tr12*tr2+tr11*tr3; ci5=ti11*ti5+ti12*ti4; ci4=ti12*ti5-ti11*ti4; ch[(k+l1)*ido]=cr2-ci5; ch[(k+2*l1)*ido]=cr3-ci4; ch[(k+3*l1)*ido]=cr3+ci4; ch[(k+4*l1)*ido]=cr2+ci5; } if(ido==1) return; for(k=0; k<l1;++k) { for(i=2; i<ido; i+=2) { ic=ido-i; ti5=cc[i+(5*k+2)*ido]+cc[ic+(5*k+1)*ido]; ti2=cc[i+(5*k+2)*ido]-cc[ic+(5*k+1)*ido]; ti4=cc[i+(5*k+4)*ido]+cc[ic+(5*k+3)*ido]; ti3=cc[i+(5*k+4)*ido]-cc[ic+(5*k+3)*ido]; tr5=cc[i-1+(5*k+2)*ido]-cc[ic-1+(5*k+1)*ido]; tr2=cc[i-1+(5*k+2)*ido]+cc[ic-1+(5*k+1)*ido]; tr4=cc[i-1+(5*k+4)*ido]-cc[ic-1+(5*k+3)*ido]; tr3=cc[i-1+(5*k+4)*ido]+cc[ic-1+(5*k+3)*ido]; ch[i-1+k*ido]=cc[i-1+5*k*ido]+tr2+tr3; ch[i+k*ido]=cc[i+5*k*ido]+ti2+ti3; cr2=cc[i-1+5*k*ido]+tr11*tr2+tr12*tr3; ci2=cc[i+5*k*ido]+tr11*ti2+tr12*ti3; cr3=cc[i-1+5*k*ido]+tr12*tr2+tr11*tr3; ci3=cc[i+5*k*ido]+tr12*ti2+tr11*ti3; cr5=ti11*tr5+ti12*tr4; ci5=ti11*ti5+ti12*ti4; cr4=ti12*tr5-ti11*tr4; ci4=ti12*ti5-ti11*ti4; dr3=cr3-ci4; dr4=cr3+ci4; di3=ci3+cr4; di4=ci3-cr4; dr5=cr2+ci5; dr2=cr2-ci5; di5=ci2-cr5; di2=ci2+cr5; ch[i-1+(k+l1)*ido] = wtable[i-2+iw1]*dr2 -wtable[i-1+iw1]*di2; ch[i+(k+l1)*ido] = wtable[i-2+iw1]*di2 +wtable[i-1+iw1]*dr2; ch[i-1+(k+2*l1)*ido] = wtable[i-2+iw2]*dr3 -wtable[i-1+iw2]*di3; ch[i+(k+2*l1)*ido] = wtable[i-2+iw2]*di3 +wtable[i-1+iw2]*dr3; ch[i-1+(k+3*l1)*ido] = wtable[i-2+iw3]*dr4 -wtable[i-1+iw3]*di4; ch[i+(k+3*l1)*ido] = wtable[i-2+iw3]*di4 +wtable[i-1+iw3]*dr4; ch[i-1+(k+4*l1)*ido] = wtable[i-2+iw4]*dr5 -wtable[i-1+iw4]*di5; ch[i+(k+4*l1)*ido] = wtable[i-2+iw4]*di5 +wtable[i-1+iw4]*dr5; } } } // ******************************************************************** // // Private Constants. // ******************************************************************** // private static final int[] NTRY_H = { 4, 2, 3, 5 }; private static final double TWO_PI = 2.0 * Math.PI; private static final double SQRT_2 = 1.414213562373095; private static final double TAU_R = -0.5; private static final double TAU_I = 0.866025403784439; // ******************************************************************** // // Private Data. // ******************************************************************** // // Working data array for FFT. private double[] tempData = null; } package ca.uol.aig.fftpack; /** * sine FFT transform of a real odd sequence. * @author Baoshe Zhang * @author Astronomical Instrument Group of University of Lethbridge. */ public class RealDoubleFFT_Odd extends RealDoubleFFT_Mixed { /** * <em>norm_factor</em> can be used to normalize this FFT transform. This is because * a call of forward transform (<em>ft</em>) followed by a call of backward * transform (<em>bt</em>) will multiply the input sequence by <em>norm_factor</em>. */ public double norm_factor; private double wavetable[]; private int ndim; /** * Construct a wavenumber table with size <em>n</em>. * The sequences with the same size can share a wavenumber table. The prime * factorization of <em>n</em> together with a tabulation of the trigonometric functions * are computed and stored. * * @param n the size of a real data sequence. When (<em>n</em>+1) is a multiplication of small * numbers (4, 2, 3, 5), this FFT transform is very efficient. */ public RealDoubleFFT_Odd(int n) { ndim = n; norm_factor = 2*(n+1); int wtable_length = 2*ndim + ndim/2 + 3 + 15; if(wavetable == null || wavetable.length != wtable_length) { wavetable = new double[wtable_length]; } sinti(ndim, wavetable); } /** * Forward sine FFT transform. It computes the discrete sine transform of * an odd sequence. * * @param x an array which contains the sequence to be transformed. After FFT, * <em>x</em> contains the transform coeffients. */ public void ft(double x[]) { sint(ndim, x, wavetable); } /** * Backward sine FFT transform. It is the unnormalized inverse transform of <em>ft</em>. * * @param x an array which contains the sequence to be transformed. After FFT, * <em>x</em> contains the transform coeffients. */ public void bt(double x[]) { sint(ndim, x, wavetable); } /*--------------------------------------- sint1: further processing of sine FFT. --------------------------------------*/ void sint1(int n, double war[], double wtable[]) { final double sqrt3=1.73205080756888; int modn, i, k; double xhold, t1, t2; int kc, np1, ns2; int iw1, iw2, iw3; double[] wtable_p1 = new double[2*(n+1)+15]; iw1=n / 2; iw2=iw1+n+1; iw3=iw2+n+1; double[] x = new double[n+1]; for(i=0; i<n; i++) { wtable[i+iw1]=war[i]; war[i]=wtable[i+iw2]; } if(n<2) { wtable[0+iw1]+=wtable[0+iw1]; } else if(n==2) { xhold=sqrt3*(wtable[0+iw1]+wtable[1+iw1]); wtable[1+iw1]=sqrt3*(wtable[0+iw1]-wtable[1+iw1]); wtable[0+iw1]=xhold; } else { np1=n+1; ns2=n / 2; wtable[0+iw2]=0; for(k=0; k<ns2; k++) { kc=n-k-1; t1=wtable[k+iw1]-wtable[kc+iw1]; t2=wtable[k]*(wtable[k+iw1]+wtable[kc+iw1]); wtable[k+1+iw2]=t1+t2; wtable[kc+1+iw2]=t2-t1; } modn=n%2; if(modn !=0) wtable[ns2+1+iw2]=4*wtable[ns2+iw1]; System.arraycopy(wtable, iw1, wtable_p1, 0, n+1); System.arraycopy(war, 0, wtable_p1, n+1, n); System.arraycopy(wtable, iw3, wtable_p1, 2*(n+1), 15); System.arraycopy(wtable, iw2, x, 0, n+1); rfftf1(np1, x, wtable_p1, 0); System.arraycopy(x, 0, wtable, iw2, n+1); wtable[0+iw1]=0.5*wtable[0+iw2]; for(i=2; i<n; i+=2) { wtable[i-1+iw1]=-wtable[i+iw2]; wtable[i+iw1]=wtable[i-2+iw1]+wtable[i-1+iw2]; } if(modn==0) wtable[n-1+iw1]=-wtable[n+iw2]; } for(i=0; i<n; i++) { wtable[i+iw2]=war[i]; war[i]=wtable[i+iw1]; } } /*---------------- sint: sine FFT ---------------*/ void sint(int n, double x[], double wtable[]) { sint1(n, x, wtable); } /*---------------------------------------------------------------------- sinti: initialization of sin-FFT ----------------------------------------------------------------------*/ void sinti(int n, double wtable[]) { final double pi=Math.PI; //3.14159265358979; int k, ns2; double dt; if(n<=1) return; ns2=n / 2; dt=pi /(double)(n+1); for(k=0; k<ns2; k++) wtable[k]=2*Math.sin((k+1)*dt); rffti1(n+1, wtable, ns2); } } package ca.uol.aig.fftpack; /** * FFT transform of a complex periodic sequence. * @author Baoshe Zhang * @author Astronomical Instrument Group of University of Lethbridge. */ public class ComplexDoubleFFT extends ComplexDoubleFFT_Mixed { /** * <em>norm_factor</em> can be used to normalize this FFT transform. This is because * a call of forward transform (<em>ft</em>) followed by a call of backward transform * (<em>bt</em>) will multiply the input sequence by <em>norm_factor</em>. */ public double norm_factor; private double wavetable[]; private int ndim; /** * Construct a wavenumber table with size <em>n</em> for Complex FFT. * The sequences with the same size can share a wavenumber table. The prime * factorization of <em>n</em> together with a tabulation of the trigonometric functions * are computed and stored. * * @param n the size of a complex data sequence. When <em>n</em> is a multiplication of small * numbers (4, 2, 3, 5), this FFT transform is very efficient. */ public ComplexDoubleFFT(int n) { ndim = n; norm_factor = n; if(wavetable == null || wavetable.length !=(4*ndim+15)) { wavetable = new double[4*ndim + 15]; } cffti(ndim, wavetable); } /** * Forward complex FFT transform. * * @param x 2*<em>n</em> real double data representing <em>n</em> complex double data. * As an input parameter, <em>x</em> is an array of 2*<em>n</em> real * data representing <em>n</em> complex data. As an output parameter, <em>x</em> represents <em>n</em> * FFT'd complex data. Their relation as follows: * <br> * x[2*i] is the real part of <em>i</em>-th complex data; * <br> * x[2*i+1] is the imaginary part of <em>i</em>-the complex data. * */ public void ft(double x[]) { if(x.length != 2*ndim) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); cfftf(ndim, x, wavetable); } /** * Forward complex FFT transform. * * @param x an array of <em>n</em> Complex data */ public void ft(Complex1D x) { if(x.x.length != ndim) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); double[] y = new double[2*ndim]; for(int i=0; i<ndim; i++) { y[2*i] = x.x[i]; y[2*i+1] = x.y[i]; } cfftf(ndim, y, wavetable); for(int i=0; i<ndim; i++) { x.x[i]=y[2*i]; x.y[i]=y[2*i+1]; } } /** * Backward complex FFT transform. It is the unnormalized inverse transform of <em>ft</em>(double[]). * * @param x 2*<em>n</em> real double data representing <em>n</em> complex double data. * * As an input parameter, <em>x</em> is an array of 2*<em>n</em> * real data representing <em>n</em> complex data. As an output parameter, <em>x</em> represents * <em>n</em> FFT'd complex data. Their relation as follows: * <br> * x[2*<em>i</em>] is the real part of <em>i</em>-th complex data; * <br> * x[2*<em>i</em>+1] is the imaginary part of <em>i</em>-the complex data. * */ public void bt(double x[]) { if(x.length != 2*ndim) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); cfftb(ndim, x, wavetable); } /** * Backward complex FFT transform. It is the unnormalized inverse transform of <em>ft</em>(Complex1D[]). * * * @param x an array of <em>n</em> Complex data */ public void bt(Complex1D x) { if(x.x.length != ndim) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); double[] y = new double[2*ndim]; for(int i=0; i<ndim; i++) { y[2*i] = x.x[i]; y[2*i+1] = x.y[i]; } cfftb(ndim, y, wavetable); for(int i=0; i<ndim; i++) { x.x[i]=y[2*i]; x.y[i]=y[2*i+1]; } } } package ca.uol.aig.fftpack; /** * @author Baoshe Zhang * @author Astronomical Instrument Group of University of Lethbridge. */ class ComplexDoubleFFT_Mixed { /*---------------------------------------------------------------------- passf2: Complex FFT's forward/backward processing of factor 2; isign is +1 for backward and -1 for forward transforms ----------------------------------------------------------------------*/ void passf2(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset, int isign) /*isign==+1 for backward transform*/ { int i, k, ah, ac; double ti2, tr2; int iw1; iw1 = offset; if(ido<=2) { for(k=0; k<l1; k++) { ah=k*ido; ac=2*k*ido; ch[ah]=cc[ac]+cc[ac+ido]; ch[ah+ido*l1]=cc[ac]-cc[ac+ido]; ch[ah+1]=cc[ac+1]+cc[ac+ido+1]; ch[ah+ido*l1+1]=cc[ac+1]-cc[ac+ido+1]; } } else { for(k=0; k<l1; k++) { for(i=0; i<ido-1; i+=2) { ah=i+k*ido; ac=i+2*k*ido; ch[ah]=cc[ac]+cc[ac+ido]; tr2=cc[ac]-cc[ac+ido]; ch[ah+1]=cc[ac+1]+cc[ac+1+ido]; ti2=cc[ac+1]-cc[ac+1+ido]; ch[ah+l1*ido+1]=wtable[i+iw1]*ti2+isign*wtable[i+1+iw1]*tr2; ch[ah+l1*ido]=wtable[i+iw1]*tr2-isign*wtable[i+1+iw1]*ti2; } } } } /*---------------------------------------------------------------------- passf3: Complex FFT's forward/backward processing of factor 3; isign is +1 for backward and -1 for forward transforms ----------------------------------------------------------------------*/ void passf3(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset, int isign) { final double taur=-0.5; final double taui=0.866025403784439; int i, k, ac, ah; double ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2; int iw1, iw2; iw1 = offset; iw2 = iw1 + ido; if(ido==2) { for(k=1; k<=l1; k++) { ac=(3*k-2)*ido; tr2=cc[ac]+cc[ac+ido]; cr2=cc[ac-ido]+taur*tr2; ah=(k-1)*ido; ch[ah]=cc[ac-ido]+tr2; ti2=cc[ac+1]+cc[ac+ido+1]; ci2=cc[ac-ido+1]+taur*ti2; ch[ah+1]=cc[ac-ido+1]+ti2; cr3=isign*taui*(cc[ac]-cc[ac+ido]); ci3=isign*taui*(cc[ac+1]-cc[ac+ido+1]); ch[ah+l1*ido]=cr2-ci3; ch[ah+2*l1*ido]=cr2+ci3; ch[ah+l1*ido+1]=ci2+cr3; ch[ah+2*l1*ido+1]=ci2-cr3; } } else { for(k=1; k<=l1; k++) { for(i=0; i<ido-1; i+=2) { ac=i+(3*k-2)*ido; tr2=cc[ac]+cc[ac+ido]; cr2=cc[ac-ido]+taur*tr2; ah=i+(k-1)*ido; ch[ah]=cc[ac-ido]+tr2; ti2=cc[ac+1]+cc[ac+ido+1]; ci2=cc[ac-ido+1]+taur*ti2; ch[ah+1]=cc[ac-ido+1]+ti2; cr3=isign*taui*(cc[ac]-cc[ac+ido]); ci3=isign*taui*(cc[ac+1]-cc[ac+ido+1]); dr2=cr2-ci3; dr3=cr2+ci3; di2=ci2+cr3; di3=ci2-cr3; ch[ah+l1*ido+1]=wtable[i+iw1]*di2+isign*wtable[i+1+iw1]*dr2; ch[ah+l1*ido]=wtable[i+iw1]*dr2-isign*wtable[i+1+iw1]*di2; ch[ah+2*l1*ido+1]=wtable[i+iw2]*di3+isign*wtable[i+1+iw2]*dr3; ch[ah+2*l1*ido]=wtable[i+iw2]*dr3-isign*wtable[i+1+iw2]*di3; } } } } /*---------------------------------------------------------------------- passf4: Complex FFT's forward/backward processing of factor 4; isign is +1 for backward and -1 for forward transforms ----------------------------------------------------------------------*/ void passf4(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset, int isign) { int i, k, ac, ah; double ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4; int iw1, iw2, iw3; iw1 = offset; iw2 = iw1 + ido; iw3 = iw2 + ido; if(ido==2) { for(k=0; k<l1; k++) { ac=4*k*ido+1; ti1=cc[ac]-cc[ac+2*ido]; ti2=cc[ac]+cc[ac+2*ido]; tr4=cc[ac+3*ido]-cc[ac+ido]; ti3=cc[ac+ido]+cc[ac+3*ido]; tr1=cc[ac-1]-cc[ac+2*ido-1]; tr2=cc[ac-1]+cc[ac+2*ido-1]; ti4=cc[ac+ido-1]-cc[ac+3*ido-1]; tr3=cc[ac+ido-1]+cc[ac+3*ido-1]; ah=k*ido; ch[ah]=tr2+tr3; ch[ah+2*l1*ido]=tr2-tr3; ch[ah+1]=ti2+ti3; ch[ah+2*l1*ido+1]=ti2-ti3; ch[ah+l1*ido]=tr1+isign*tr4; ch[ah+3*l1*ido]=tr1-isign*tr4; ch[ah+l1*ido+1]=ti1+isign*ti4; ch[ah+3*l1*ido+1]=ti1-isign*ti4; } } else { for(k=0; k<l1; k++) { for(i=0; i<ido-1; i+=2) { ac=i+1+4*k*ido; ti1=cc[ac]-cc[ac+2*ido]; ti2=cc[ac]+cc[ac+2*ido]; ti3=cc[ac+ido]+cc[ac+3*ido]; tr4=cc[ac+3*ido]-cc[ac+ido]; tr1=cc[ac-1]-cc[ac+2*ido-1]; tr2=cc[ac-1]+cc[ac+2*ido-1]; ti4=cc[ac+ido-1]-cc[ac+3*ido-1]; tr3=cc[ac+ido-1]+cc[ac+3*ido-1]; ah=i+k*ido; ch[ah]=tr2+tr3; cr3=tr2-tr3; ch[ah+1]=ti2+ti3; ci3=ti2-ti3; cr2=tr1+isign*tr4; cr4=tr1-isign*tr4; ci2=ti1+isign*ti4; ci4=ti1-isign*ti4; ch[ah+l1*ido]=wtable[i+iw1]*cr2-isign*wtable[i+1+iw1]*ci2; ch[ah+l1*ido+1]=wtable[i+iw1]*ci2+isign*wtable[i+1+iw1]*cr2; ch[ah+2*l1*ido]=wtable[i+iw2]*cr3-isign*wtable[i+1+iw2]*ci3; ch[ah+2*l1*ido+1]=wtable[i+iw2]*ci3+isign*wtable[i+1+iw2]*cr3; ch[ah+3*l1*ido]=wtable[i+iw3]*cr4-isign*wtable[i+1+iw3]*ci4; ch[ah+3*l1*ido+1]=wtable[i+iw3]*ci4+isign*wtable[i+1+iw3]*cr4; } } } } /*---------------------------------------------------------------------- passf5: Complex FFT's forward/backward processing of factor 5; isign is +1 for backward and -1 for forward transforms ----------------------------------------------------------------------*/ void passf5(int ido, int l1, final double cc[], double ch[], final double wtable[], int offset, int isign) /*isign==-1 for forward transform and+1 for backward transform*/ { final double tr11=0.309016994374947; final double ti11=0.951056516295154; final double tr12=-0.809016994374947; final double ti12=0.587785252292473; int i, k, ac, ah; double ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3, ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5; int iw1, iw2, iw3, iw4; iw1 = offset; iw2 = iw1 + ido; iw3 = iw2 + ido; iw4 = iw3 + ido; if(ido==2) { for(k=1; k<=l1;++k) { ac=(5*k-4)*ido+1; ti5=cc[ac]-cc[ac+3*ido]; ti2=cc[ac]+cc[ac+3*ido]; ti4=cc[ac+ido]-cc[ac+2*ido]; ti3=cc[ac+ido]+cc[ac+2*ido]; tr5=cc[ac-1]-cc[ac+3*ido-1]; tr2=cc[ac-1]+cc[ac+3*ido-1]; tr4=cc[ac+ido-1]-cc[ac+2*ido-1]; tr3=cc[ac+ido-1]+cc[ac+2*ido-1]; ah=(k-1)*ido; ch[ah]=cc[ac-ido-1]+tr2+tr3; ch[ah+1]=cc[ac-ido]+ti2+ti3; cr2=cc[ac-ido-1]+tr11*tr2+tr12*tr3; ci2=cc[ac-ido]+tr11*ti2+tr12*ti3; cr3=cc[ac-ido-1]+tr12*tr2+tr11*tr3; ci3=cc[ac-ido]+tr12*ti2+tr11*ti3; cr5=isign*(ti11*tr5+ti12*tr4); ci5=isign*(ti11*ti5+ti12*ti4); cr4=isign*(ti12*tr5-ti11*tr4); ci4=isign*(ti12*ti5-ti11*ti4); ch[ah+l1*ido]=cr2-ci5; ch[ah+4*l1*ido]=cr2+ci5; ch[ah+l1*ido+1]=ci2+cr5; ch[ah+2*l1*ido+1]=ci3+cr4; ch[ah+2*l1*ido]=cr3-ci4; ch[ah+3*l1*ido]=cr3+ci4; ch[ah+3*l1*ido+1]=ci3-cr4; ch[ah+4*l1*ido+1]=ci2-cr5; } } else { for(k=1; k<=l1; k++) { for(i=0; i<ido-1; i+=2) { ac=i+1+(k*5-4)*ido; ti5=cc[ac]-cc[ac+3*ido]; ti2=cc[ac]+cc[ac+3*ido]; ti4=cc[ac+ido]-cc[ac+2*ido]; ti3=cc[ac+ido]+cc[ac+2*ido]; tr5=cc[ac-1]-cc[ac+3*ido-1]; tr2=cc[ac-1]+cc[ac+3*ido-1]; tr4=cc[ac+ido-1]-cc[ac+2*ido-1]; tr3=cc[ac+ido-1]+cc[ac+2*ido-1]; ah=i+(k-1)*ido; ch[ah]=cc[ac-ido-1]+tr2+tr3; ch[ah+1]=cc[ac-ido]+ti2+ti3; cr2=cc[ac-ido-1]+tr11*tr2+tr12*tr3; ci2=cc[ac-ido]+tr11*ti2+tr12*ti3; cr3=cc[ac-ido-1]+tr12*tr2+tr11*tr3; ci3=cc[ac-ido]+tr12*ti2+tr11*ti3; cr5=isign*(ti11*tr5+ti12*tr4); ci5=isign*(ti11*ti5+ti12*ti4); cr4=isign*(ti12*tr5-ti11*tr4); ci4=isign*(ti12*ti5-ti11*ti4); dr3=cr3-ci4; dr4=cr3+ci4; di3=ci3+cr4; di4=ci3-cr4; dr5=cr2+ci5; dr2=cr2-ci5; di5=ci2-cr5; di2=ci2+cr5; ch[ah+l1*ido]=wtable[i+iw1]*dr2-isign*wtable[i+1+iw1]*di2; ch[ah+l1*ido+1]=wtable[i+iw1]*di2+isign*wtable[i+1+iw1]*dr2; ch[ah+2*l1*ido]=wtable[i+iw2]*dr3-isign*wtable[i+1+iw2]*di3; ch[ah+2*l1*ido+1]=wtable[i+iw2]*di3+isign*wtable[i+1+iw2]*dr3; ch[ah+3*l1*ido]=wtable[i+iw3]*dr4-isign*wtable[i+1+iw3]*di4; ch[ah+3*l1*ido+1]=wtable[i+iw3]*di4+isign*wtable[i+1+iw3]*dr4; ch[ah+4*l1*ido]=wtable[i+iw4]*dr5-isign*wtable[i+1+iw4]*di5; ch[ah+4*l1*ido+1]=wtable[i+iw4]*di5+isign*wtable[i+1+iw4]*dr5; } } } } /*---------------------------------------------------------------------- passfg: Complex FFT's forward/backward processing of general factor; isign is +1 for backward and -1 for forward transforms ----------------------------------------------------------------------*/ void passfg(int nac[], int ido, int ip, int l1, int idl1, final double cc[], double c1[], double c2[], double ch[], double ch2[], final double wtable[], int offset, int isign) { int idij, idlj, idot, ipph, i, j, k, l, jc, lc, ik, idj, idl, inc, idp; double wai, war; int iw1; iw1 = offset; idot=ido / 2; ipph=(ip+1)/ 2; idp=ip*ido; if(ido>=l1) { for(j=1; j<ipph; j++) { jc=ip-j; for(k=0; k<l1; k++) { for(i=0; i<ido; i++) { ch[i+(k+j*l1)*ido]=cc[i+(j+k*ip)*ido]+cc[i+(jc+k*ip)*ido]; ch[i+(k+jc*l1)*ido]=cc[i+(j+k*ip)*ido]-cc[i+(jc+k*ip)*ido]; } } } for(k=0; k<l1; k++) for(i=0; i<ido; i++) ch[i+k*ido]=cc[i+k*ip*ido]; } else { for(j=1; j<ipph; j++) { jc=ip-j; for(i=0; i<ido; i++) { for(k=0; k<l1; k++) { ch[i+(k+j*l1)*ido]=cc[i+(j+k*ip)*ido]+cc[i+(jc+k*ip)*ido]; ch[i+(k+jc*l1)*ido]=cc[i+(j+k*ip)*ido]-cc[i+(jc+k*ip)*ido]; } } } for(i=0; i<ido; i++) for(k=0; k<l1; k++) ch[i+k*ido]=cc[i+k*ip*ido]; } idl=2-ido; inc=0; for(l=1; l<ipph; l++) { lc=ip-l; idl+=ido; for(ik=0; ik<idl1; ik++) { c2[ik+l*idl1]=ch2[ik]+wtable[idl-2+iw1]*ch2[ik+idl1]; c2[ik+lc*idl1]=isign*wtable[idl-1+iw1]*ch2[ik+(ip-1)*idl1]; } idlj=idl; inc+=ido; for(j=2; j<ipph; j++) { jc=ip-j; idlj+=inc; if(idlj>idp) idlj-=idp; war=wtable[idlj-2+iw1]; wai=wtable[idlj-1+iw1]; for(ik=0; ik<idl1; ik++) { c2[ik+l*idl1]+=war*ch2[ik+j*idl1]; c2[ik+lc*idl1]+=isign*wai*ch2[ik+jc*idl1]; } } } for(j=1; j<ipph; j++) for(ik=0; ik<idl1; ik++) ch2[ik]+=ch2[ik+j*idl1]; for(j=1; j<ipph; j++) { jc=ip-j; for(ik=1; ik<idl1; ik+=2) { ch2[ik-1+j*idl1]=c2[ik-1+j*idl1]-c2[ik+jc*idl1]; ch2[ik-1+jc*idl1]=c2[ik-1+j*idl1]+c2[ik+jc*idl1]; ch2[ik+j*idl1]=c2[ik+j*idl1]+c2[ik-1+jc*idl1]; ch2[ik+jc*idl1]=c2[ik+j*idl1]-c2[ik-1+jc*idl1]; } } nac[0]=1; if(ido==2) return; nac[0]=0; for(ik=0; ik<idl1; ik++) c2[ik]=ch2[ik]; for(j=1; j<ip; j++) { for(k=0; k<l1; k++) { c1[(k+j*l1)*ido+0]=ch[(k+j*l1)*ido+0]; c1[(k+j*l1)*ido+1]=ch[(k+j*l1)*ido+1]; } } if(idot<=l1) { idij=0; for(j=1; j<ip; j++) { idij+=2; for(i=3; i<ido; i+=2) { idij+=2; for(k=0; k<l1; k++) { c1[i-1+(k+j*l1)*ido]= wtable[idij-2+iw1]*ch[i-1+(k+j*l1)*ido]- isign*wtable[idij-1+iw1]*ch[i+(k+j*l1)*ido]; c1[i+(k+j*l1)*ido]= wtable[idij-2+iw1]*ch[i+(k+j*l1)*ido]+ isign*wtable[idij-1+iw1]*ch[i-1+(k+j*l1)*ido]; } } } } else { idj=2-ido; for(j=1; j<ip; j++) { idj+=ido; for(k=0; k<l1; k++) { idij=idj; for(i=3; i<ido; i+=2) { idij+=2; c1[i-1+(k+j*l1)*ido]= wtable[idij-2+iw1]*ch[i-1+(k+j*l1)*ido]- isign*wtable[idij-1+iw1]*ch[i+(k+j*l1)*ido]; c1[i+(k+j*l1)*ido]= wtable[idij-2+iw1]*ch[i+(k+j*l1)*ido]+ isign*wtable[idij-1+iw1]*ch[i-1+(k+j*l1)*ido]; } } } } } /*--------------------------------------------------------- cfftf1: further processing of Complex forward FFT --------------------------------------------------------*/ void cfftf1(int n, double c[], final double wtable[], int isign) { int idot, i; int k1, l1, l2; int na, nf, ip, iw, ido, idl1; int[] nac = new int[1]; int iw1, iw2; double[] ch = new double[2*n]; iw1=2*n; iw2=4*n; System.arraycopy(wtable, 0, ch, 0, 2*n); nac[0] = 0; nf=(int)wtable[1+iw2]; na=0; l1=1; iw=iw1; for(k1=2; k1<=nf+1; k1++) { ip=(int)wtable[k1+iw2]; l2=ip*l1; ido=n / l2; idot=ido+ido; idl1=idot*l1; if(ip==4) { if(na==0) { passf4(idot, l1, c, ch, wtable, iw, isign); } else { passf4(idot, l1, ch, c, wtable, iw, isign); } na=1-na; } else if(ip==2) { if(na==0) { passf2(idot, l1, c, ch, wtable, iw, isign); } else { passf2(idot, l1, ch, c, wtable, iw, isign); } na=1-na; } else if(ip==3) { if(na==0) { passf3(idot, l1, c, ch, wtable, iw, isign); } else { passf3(idot, l1, ch, c, wtable, iw, isign); } na=1-na; } else if(ip==5) { if(na==0) { passf5(idot, l1, c, ch, wtable, iw, isign); } else { passf5(idot, l1, ch, c, wtable, iw, isign); } na=1-na; } else { if(na==0) { passfg(nac, idot, ip, l1, idl1, c, c, c, ch, ch, wtable, iw, isign); } else { passfg(nac, idot, ip, l1, idl1, ch, ch, ch, c, c, wtable, iw, isign); } if(nac[0] !=0) na=1-na; } l1=l2; iw+=(ip-1)*idot; } if(na==0) return; for(i=0; i<2*n; i++) c[i]=ch[i]; } /*--------------------------------------------------------- cfftf: Complex forward FFT --------------------------------------------------------*/ void cfftf(int n, double c[], double wtable[]) { cfftf1(n, c, wtable, -1); } /*--------------------------------------------------------- cfftb: Complex borward FFT --------------------------------------------------------*/ void cfftb(int n, double c[], double wtable[]) { cfftf1(n, c, wtable, +1); } /*--------------------------------------------------------- cffti1: further initialization of Complex FFT --------------------------------------------------------*/ void cffti1(int n, double wtable[]) { final int[] ntryh = {3, 4, 2, 5}; final double twopi=2.0D*Math.PI; double argh; int idot, ntry=0, i, j; double argld; int i1, k1, l1, l2, ib; double fi; int ld, ii, nf, ip, nl, nq, nr; double arg; int ido, ipm; nl=n; nf=0; j=0; factorize_loop: while(true) { j++; if(j<=4) ntry=ntryh[j-1]; else ntry+=2; do { nq=nl / ntry; nr=nl-ntry*nq; if(nr !=0) continue factorize_loop; nf++; wtable[nf+1+4*n]=ntry; nl=nq; if(ntry==2 && nf !=1) { for(i=2; i<=nf; i++) { ib=nf-i+2; wtable[ib+1+4*n]=wtable[ib+4*n]; } wtable[2+4*n]=2; } } while(nl !=1); break factorize_loop; } wtable[0+4*n]=n; wtable[1+4*n]=nf; argh=twopi /(double)n; i=1; l1=1; for(k1=1; k1<=nf; k1++) { ip=(int)wtable[k1+1+4*n]; ld=0; l2=l1*ip; ido=n / l2; idot=ido+ido+2; ipm=ip-1; for(j=1; j<=ipm; j++) { i1=i; wtable[i-1+2*n]=1; wtable[i+2*n]=0; ld+=l1; fi=0; argld=ld*argh; for(ii=4; ii<=idot; ii+=2) { i+=2; fi+=1; arg=fi*argld; wtable[i-1+2*n]=Math.cos(arg); wtable[i+2*n]=Math.sin(arg); } if(ip>5) { wtable[i1-1+2*n]=wtable[i-1+2*n]; wtable[i1+2*n]=wtable[i+2*n]; } } l1=l2; } } /*--------------------------------------------------------- cffti: Initialization of Real forward FFT --------------------------------------------------------*/ void cffti(int n, double wtable[]) { if(n==1) return; cffti1(n, wtable); } /*cffti*/ } package ca.uol.aig.fftpack; /** * FFT transform of a real periodic sequence. * @author Baoshe Zhang * @author Astronomical Instrument Group of University of Lethbridge. */ public class RealDoubleFFT extends RealDoubleFFT_Mixed { /** * Construct a wavenumber table with size <em>n</em>. * The sequences with the same size can share a wavenumber table. The prime * factorization of <em>n</em> together with a tabulation of the trigonometric functions * are computed and stored. * * @param n the size of a real data sequence. When <em>n</em> is a multiplication of small * numbers (4, 2, 3, 5), this FFT transform is very efficient. */ public RealDoubleFFT(int n) { ndim = n; norm_factor = n; if(wavetable == null || wavetable.length !=(2*ndim+15)) { wavetable = new double[2*ndim + 15]; } rffti(ndim, wavetable); } /** * Forward real FFT transform. It computes the discrete transform * of a real data sequence. * * <p>The x parameter is both input and output data. After the FFT, x * contains the transform coefficients used to construct n * complex FFT coefficients. * * <p>The real part of the first complex FFT coefficients is x[0]; * its imaginary part is 0. If n is even set m = n/2, if n is odd set * m = (n+1)/2, then for k = 1, ..., m-1: * <ul> * <li>the real part of k-th complex FFT coefficient is x[2 * k]; * <li>the imaginary part of k-th complex FFT coefficient is x[2 * k - 1]. * </ul> * * <p>If n is even, the real of part of (n/2)-th complex FFT * coefficient is x[n - 1]; its imaginary part is 0. * * <p>The remaining complex FFT coefficients can be obtained by the * symmetry relation: the (n-k)-th complex FFT coefficient is the * conjugate of n-th complex FFT coefficient. * * @param x An array which contains the sequence to be * transformed. */ public void ft(double[] x) { if (x.length != ndim) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); rfftf(ndim, x, wavetable); } /** * Forward real FFT transform. It computes the discrete transform of a real data sequence. * * @param x an array which contains the sequence to be transformed. After FFT, * <em>x</em> contains the transform coeffients used to construct <em>n</em> complex FFT coeffients. * <br> * @param y the first complex (<em>n</em>+1)/2 (when <em>n</em> is odd) or (<em>n</em>/2+1) (when * <em>n</em> is even) FFT coefficients. * The remaining complex FFT coefficients can be obtained by the symmetry relation: * the (<em>n</em>-<em>k</em>)-th complex FFT coefficient is the conjugate of <em>n</em>-th complex FFT coeffient. * */ public void ft(double x[], Complex1D y) { if (x.length != ndim) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); rfftf(ndim, x, wavetable); if(ndim%2 == 0) { y.x = new double[ndim/2 + 1]; y.y = new double[ndim/2 + 1]; } else { y.x = new double[(ndim+1)/2]; y.y = new double[(ndim+1)/2]; } y.x[0] = x[0]; y.y[0] = 0.0D; for(int i=1; i<(ndim+1)/2; i++) { y.x[i] = x[2*i-1]; y.y[i] = x[2*i]; } if(ndim%2 == 0) { y.x[ndim/2] = x[ndim-1]; y.y[ndim/2] = 0.0D; } } /** * Backward real FFT transform. It is the unnormalized inverse transform of <em>ft</em>(double[]). * * @param x an array which contains the sequence to be transformed. After FFT, * <em>x</em> contains the transform coeffients. Also see the comments of <em>ft</em>(double[]) * for the relation between <em>x</em> and complex FFT coeffients. */ public void bt(double x[]) { if(x.length != ndim) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); rfftb(ndim, x, wavetable); } /** * Backward real FFT transform. It is the unnormalized inverse transform of <em>ft</em>(Complex1D, double[]). * * @param x an array which contains the sequence to be transformed. When <em>n</em> is odd, it contains the first * (<em>n</em>+1)/2 complex data; when <em>n</em> is even, it contains (<em>n</em>/2+1) complex data. * @param y the real FFT coeffients. * <br> * Also see the comments of <em>ft</em>(double[]) for the relation * between <em>x</em> and complex FFT coeffients. */ public void bt(Complex1D x, double y[]) { if(ndim%2 == 0) { if(x.x.length != ndim/2+1) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); } else { if(x.x.length != (ndim+1)/2) throw new IllegalArgumentException("The length of data can not match that of the wavetable"); } y[0] = x.x[0]; for(int i=1; i<(ndim+1)/2; i++) { y[2*i-1]=x.x[i]; y[2*i]=x.y[i]; } if(ndim%2 == 0) { y[ndim-1]=x.x[ndim/2]; } rfftb(ndim, y, wavetable); } /** * <em>norm_factor</em> can be used to normalize this FFT transform. This is because * a call of forward transform (<em>ft</em>) followed by a call of backward transform * (<em>bt</em>) will multiply the input sequence by <em>norm_factor</em>. */ public double norm_factor; private double wavetable[]; private int ndim; } package ca.uol.aig.fftpack; /** * cosine FFT transform of a real even sequence. * @author Baoshe Zhang * @author Astronomical Instrument Group of University of Lethbridge. */ public class RealDoubleFFT_Even extends RealDoubleFFT_Mixed { /** * <em>norm_factor</em> can be used to normalize this FFT transform. This is because * a call of forward transform (<em>ft</em>) followed by a call of backward transform * (<em>bt</em>) will multiply the input sequence by <em>norm_factor</em>. */ public double norm_factor; private double wavetable[]; private int ndim; /** * Construct a wavenumber table with size <em>n</em>. * The sequences with the same size can share a wavenumber table. The prime * factorization of <em>n</em> together with a tabulation of the trigonometric functions * are computed and stored. * * @param n the size of a real data sequence. When (<em>n</em>-1) is a multiplication of small * numbers (4, 2, 3, 5), this FFT transform is very efficient. */ public RealDoubleFFT_Even(int n) { ndim = n; norm_factor = 2 * (n - 1); if (wavetable == null || wavetable.length != (3 * ndim + 15)) wavetable = new double[3 * ndim + 15]; costi(ndim, wavetable); } /** * Forward cosine FFT transform. It computes the discrete sine transform of * an odd sequence. * * @param x an array which contains the sequence to be transformed. After FFT, * <em>x</em> contains the transform coeffients. */ public void ft(double[] x) { cost(ndim, x, wavetable); } /** * Backward cosine FFT transform. It is the unnormalized inverse transform of <em>ft</em>. * * @param x an array which contains the sequence to be transformed. After FFT, * <em>x</em> contains the transform coeffients. */ public void bt(double[] x) { cost(ndim, x, wavetable); } /*------------------------------------------------------------- cost: cosine FFT. Backward and forward cos-FFT are the same. ------------------------------------------------------------*/ void cost(int n, double x[], final double wtable[]) { int modn, i, k; double c1, t1, t2; int kc; double xi; int nm1; double x1h; int ns2; double tx2, x1p3, xim2; nm1=n-1; ns2=n / 2; if(n-2<0) return; else if(n==2) { x1h=x[0]+x[1]; x[1]=x[0]-x[1]; x[0]=x1h; } else if(n==3) { x1p3=x[0]+x[2]; tx2=x[1]+x[1]; x[1]=x[0]-x[2]; x[0]=x1p3+tx2; x[2]=x1p3-tx2; } else { c1=x[0]-x[n-1]; x[0]+=x[n-1]; for(k=1; k<ns2; k++) { kc=nm1-k; t1=x[k]+x[kc]; t2=x[k]-x[kc]; c1+=wtable[kc]*t2; t2=wtable[k]*t2; x[k]=t1-t2; x[kc]=t1+t2; } modn=n%2; if(modn !=0) x[ns2]+=x[ns2]; rfftf1(nm1, x, wtable, n); xim2=x[1]; x[1]=c1; for(i=3; i<n; i+=2) { xi=x[i]; x[i]=x[i-2]-x[i-1]; x[i-1]=xim2; xim2=xi; } if(modn !=0) x[n-1]=xim2; } } /*---------------------------------- costi: initialization of cos-FFT ---------------------------------*/ void costi(int n, double wtable[]) { final double pi=Math.PI; //3.14159265358979; int k, kc, ns2; double dt; if(n<=3) return; ns2=n / 2; dt=pi /(double)(n-1); for(k=1; k<ns2; k++) { kc=n-k-1; wtable[k]=2*Math.sin(k*dt); wtable[kc]=2*Math.cos(k*dt); } rffti1(n-1, wtable, n); } } package ca.uol.aig.fftpack; /** * cosine FFT transform with odd wave numbers. * @author Baoshe Zhang * @author Astronomical Instrument Group of University of Lethbridge. */ public class RealDoubleFFT_Even_Odd extends RealDoubleFFT_Mixed { /** * <em>norm_factor</em> can be used to normalize this FFT transform. This is because * a call of forward transform (<em>ft</em>) followed by a call of backward transform * (<em>bt</em>) will multiply the input sequence by <em>norm_factor</em>. */ public double norm_factor; double wavetable[]; int ndim; /** * Construct a wavenumber table with size <em>n</em>. * The sequences with the same size can share a wavenumber table. The prime * factorization of <em>n</em> together with a tabulation of the trigonometric functions * are computed and stored. * * @param n the size of a real data sequence. When <em>n</em> is a multiplication of small * numbers(4, 2, 3, 5), this FFT transform is very efficient. */ public RealDoubleFFT_Even_Odd(int n) { ndim = n; norm_factor = 4*n; if(wavetable == null || wavetable.length !=(3*ndim+15)) { wavetable = new double[3*ndim + 15]; } cosqi(ndim, wavetable); } /** * Forward FFT transform of quarter wave data. It computes the coeffients in * cosine series representation with only odd wave numbers. * * @param x an array which contains the sequence to be transformed. After FFT, * <em>x</em> contains the transform coeffients. */ public void ft(double x[]) { cosqf(ndim, x, wavetable); } /** * Backward FFT transform of quarter wave data. It is the unnormalized inverse transform * of <em>ft</em>. * * @param x an array which contains the sequence to be tranformed. After FFT, <em>x</em> contains * the transform coeffients. */ public void bt(double x[]) { cosqb(ndim, x, wavetable); } /*---------------------------------------------------------------------- cosqf1: further processing of forward cos-FFT with odd wave numbers. ----------------------------------------------------------------------*/ void cosqf1(int n, double x[], double wtable[]) { int modn, i, k; int kc, ns2; double xim1; ns2=(n+1)/ 2; for(k=1; k<ns2; k++) { kc=n-k; wtable[k+n]=x[k]+x[kc]; wtable[kc+n]=x[k]-x[kc]; } modn=n%2; if(modn==0) wtable[ns2+n]=x[ns2]+x[ns2]; for(k=1; k<ns2; k++) { kc=n-k; x[k]=wtable[k-1]*wtable[kc+n]+wtable[kc-1]*wtable[k+n]; x[kc]=wtable[k-1]*wtable[k+n]-wtable[kc-1]*wtable[kc+n]; } if(modn==0) x[ns2]=wtable[ns2-1]*wtable[ns2+n]; rfftf1(n, x, wtable, n); for(i=2; i<n; i+=2) { xim1=x[i-1]-x[i]; x[i]=x[i-1]+x[i]; x[i-1]=xim1; } } /*---------------------------------------------------------------------- cosqb1: further processing of backward cos-FFT with odd wave numbers. ----------------------------------------------------------------------*/ void cosqb1(int n, double x[], double wtable[]) { int modn, i, k; int kc, ns2; double xim1; ns2=(n+1)/ 2; for(i=2; i<n; i+=2) { xim1=x[i-1]+x[i]; x[i]-=x[i-1]; x[i-1]=xim1; } x[0]+=x[0]; modn=n%2; if(modn==0) x[n-1]+=x[n-1]; rfftb1(n, x, wtable, n); for(k=1; k<ns2; k++) { kc=n-k; wtable[k+n]=wtable[k-1]*x[kc]+wtable[kc-1]*x[k]; wtable[kc+n]=wtable[k-1]*x[k]-wtable[kc-1]*x[kc]; } if(modn==0) x[ns2]=wtable[ns2-1]*(x[ns2]+x[ns2]); for(k=1; k<ns2; k++) { kc=n-k; x[k]=wtable[k+n]+wtable[kc+n]; x[kc]=wtable[k+n]-wtable[kc+n]; } x[0]+=x[0]; } /*----------------------------------------------- cosqf: forward cosine FFT with odd wave numbers. ----------------------------------------------*/ void cosqf(int n, double x[], final double wtable[]) { final double sqrt2=1.4142135623731; double tsqx; if(n<2) { return; } else if(n==2) { tsqx=sqrt2*x[1]; x[1]=x[0]-tsqx; x[0]+=tsqx; } else { cosqf1(n, x, wtable); } } /*----------------------------------------------- cosqb: backward cosine FFT with odd wave numbers. ----------------------------------------------*/ void cosqb(int n, double x[], double wtable[]) { final double tsqrt2=2.82842712474619; double x1; if(n<2) { x[0]*=4; } else if(n==2) { x1=4*(x[0]+x[1]); x[1]=tsqrt2*(x[0]-x[1]); x[0]=x1; } else { cosqb1(n, x, wtable); } } /*----------------------------------------------------------- cosqi: initialization of cosine FFT with odd wave numbers. ----------------------------------------------------------*/ void cosqi(int n, double wtable[]) { final double pih=Math.PI/2.0D; //1.57079632679491; int k; double dt; dt=pih / (double)n; for(k=0; k<n; k++) wtable[k]=Math.cos((k+1)*dt); rffti1(n, wtable, n); } } package ca.uol.aig.fftpack; /** * sine FFT transform with odd wave numbers. * @author Baoshe Zhang * @author Astronomical Instrument Group of University of Lethbridge. */ public class RealDoubleFFT_Odd_Odd extends RealDoubleFFT_Even_Odd { /** * <em>norm_factor</em> can be used to normalize this FFT transform. This is because * a call of forward transform (<em>ft</em>) followed by a call of backward transform * (<em>bt</em>) will multiply the input sequence by <em>norm_factor</em>. */ /** * Construct a wavenumber table with size n. * The sequences with the same size can share a wavenumber table. The prime * factorization of <em>n</em> together with a tabulation of the trigonometric functions * are computed and stored. * * @param n the size of a real data sequence. When <em>n</em> is a multiplication of small * numbers (4, 2, 3, 5), this FFT transform is very efficient. */ public RealDoubleFFT_Odd_Odd(int n) { super(n); } /** * Forward FFT transform of quarter wave data. It computes the coeffients in * sine series representation with only odd wave numbers. * * @param x an array which contains the sequence to be transformed. After FFT, * <em>x</em> contains the transform coeffients. */ @Override public void ft(double x[]) { sinqf(ndim, x, wavetable); } /** * Backward FFT transform of quarter wave data. It is the unnormalized inverse transform * of <em>ft</em>. * * @param x an array which contains the sequence to be tranformed. After FFT, <em>x</em> contains * the transform coeffients. */ @Override public void bt(double x[]) { sinqb(ndim, x, wavetable); } /*----------------------------------------------- sinqf: forward sine FFT with odd wave numbers. ----------------------------------------------*/ void sinqf(int n, double x[], double wtable[]) { int k; double xhold; int kc, ns2; if(n==1) return; ns2=n / 2; for(k=0; k<ns2; k++) { kc=n-k-1; xhold=x[k]; x[k]=x[kc]; x[kc]=xhold; } cosqf(n, x, wtable); for(k=1; k<n; k+=2) x[k]=-x[k]; } /*----------------------------------------------- sinqb: backward sine FFT with odd wave numbers. ----------------------------------------------*/ void sinqb(int n, double x[], double wtable[]) { int k; double xhold; int kc, ns2; if(n<=1) { x[0]*=4; return; } ns2=n / 2; for(k=1; k<n; k+=2) x[k]=-x[k]; cosqb(n, x, wtable); for(k=0; k<ns2; k++) { kc=n-k-1; xhold=x[k]; x[k]=x[kc]; x[kc]=xhold; } } /* void sinqi(int n, double wtable[]) { cosqi(n, wtable); } */ }
Audio Synthesis
package app.test; import android.app.Activity; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class Test extends Activity implements OnClickListener { Button startSound,endSound; AudioSynthesisTask audioSynth; boolean keepGoing = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startSound = (Button) this.findViewById(R.id.StartSound); startSound.setOnClickListener(this); endSound = (Button) this.findViewById(R.id.EndSound); endSound.setOnClickListener(this); endSound.setEnabled(false); } @Override public void onPause() { super.onPause(); keepGoing = false; endSound.setEnabled(false); startSound.setEnabled(true); } public void onClick(View v) { if (v == startSound) { keepGoing = true; audioSynth = new AudioSynthesisTask(); audioSynth.execute(); endSound.setEnabled(true); startSound.setEnabled(false); } else if (v == endSound) { keepGoing = false; endSound.setEnabled(false); startSound.setEnabled(true); } } private class AudioSynthesisTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { final int SAMPLE_RATE = 11025; int minSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM); audioTrack.play(); short[] buffer = { 1,2,3,4}; while (keepGoing) { audioTrack.write(buffer, 0, buffer.length); } return null; } } } //main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/StartSound" android:text="Start Sound"></Button> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/EndSound" android:text="End Sound"></Button> </LinearLayout>
Audio Synthesis 440 Hz
package app.test; import android.app.Activity; import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioTrack; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class Test extends Activity implements OnClickListener { Button startSound; Button endSound; AudioSynthesisTask audioSynth; boolean keepGoing = false; float synth_frequency = 440; // 440 Hz, Middle A @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); startSound = (Button) this.findViewById(R.id.StartSound); startSound.setOnClickListener(this); endSound = (Button) this.findViewById(R.id.EndSound); endSound.setOnClickListener(this); endSound.setEnabled(false); } @Override public void onPause() { super.onPause(); keepGoing = false; endSound.setEnabled(false); startSound.setEnabled(true); } public void onClick(View v) { if (v == startSound) { keepGoing = true; audioSynth = new AudioSynthesisTask(); audioSynth.execute(); endSound.setEnabled(true); startSound.setEnabled(false); } else if (v == endSound) { keepGoing = false; endSound.setEnabled(false); startSound.setEnabled(true); } } private class AudioSynthesisTask extends AsyncTask<Void, Void, Void> { @Override protected Void doInBackground(Void... params) { final int SAMPLE_RATE = 11025; int minSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT); AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT, minSize, AudioTrack.MODE_STREAM); audioTrack.play(); short[] buffer = new short[minSize]; float angular_frequency = (float) (2 * Math.PI) * synth_frequency / SAMPLE_RATE; float angle = 0; while (keepGoing) { for (int i = 0; i < buffer.length; i++) { buffer[i] = (short) (Short.MAX_VALUE * ((float) Math .sin(angle))); angle += angular_frequency; } audioTrack.write(buffer, 0, buffer.length); } return null; } } } //main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/StartSound" android:text="Start Sound"></Button> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/EndSound" android:text="End Sound"></Button> </LinearLayout>
Audio Files Filter
import java.io.File; import java.io.FilenameFilter; public class AudioFilesFilter implements FilenameFilter { private final static String[] acceptedExtensions= {"mp3", "mp2", "wav", "flac", "ogg", "au" , "snd", "mid", "midi", "kar" , "mga", "aif", "aiff", "aifc", "m3u", "oga", "spx"}; @Override public boolean accept(File dir, String filename) { for (int _i=0; _i < acceptedExtensions.length ; _i++){ if (filename.endsWith("." + acceptedExtensions[_i])) return true; } return false; } }
Recording Audio
//package com.zyc.misc; import android.content.Context; import android.media.MediaPlayer; import android.media.MediaRecorder; import java.io.File; import java.io.FileInputStream; import java.io.IOException; /** * @author ZengYongchang@gmail.com */ class AudioUtil { private MediaRecorder mMediaRecorder; public void startRecordingAudio(Context context, String audioFileName) { try { mMediaRecorder = new MediaRecorder(); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); mMediaRecorder.setOutputFile(context.openFileOutput(audioFileName, Context.MODE_PRIVATE).getFD()); mMediaRecorder.prepare(); mMediaRecorder.start(); } catch (IOException e) { e.printStackTrace(); } } public void stopRecordingAudio() { mMediaRecorder.stop(); mMediaRecorder.release(); mMediaRecorder = null; } public void playAudio(String audioFilePath) { try { MediaPlayer mp = new MediaPlayer(); mp.reset(); mp.setDataSource((new FileInputStream(new File(audioFilePath))).getFD()); mp.prepare(); mp.start(); } catch (IOException e) { e.printStackTrace(); } } }
AudioClip
import android.content.Context; import android.media.MediaPlayer; public class AudioClip { private MediaPlayer mPlayer; private String name; private boolean mPlaying = false; private boolean mLoop = false; public AudioClip(Context ctx, int resID) { name = ctx.getResources().getResourceName(resID); mPlayer = MediaPlayer.create(ctx, resID); mPlayer.setVolume(1000, 1000); mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { public void onCompletion(MediaPlayer mp) { mPlaying = false; if(mLoop) mp.start(); } }); } public synchronized void play(){ if(mPlaying) return; if(mPlayer != null){ mPlaying = true; mPlayer.start(); } } public synchronized void stop(){ try{ mLoop = false; if(mPlaying){ mPlaying = false; mPlayer.pause(); } }catch(Exception e){ System.err.println("AudioClip::stop "+name + " " + e.toString()); } } public synchronized void loop () { mLoop = true; mPlaying = true; mPlayer.start(); } public void release () { if(mPlayer != null){ mPlayer.release(); mPlayer = null; } } }
Random Media Player
package com.example.android.musicplayer; import android.content.Context; import android.media.AudioManager; public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener { AudioManager mAM; MusicFocusable mFocusable; public AudioFocusHelper(Context ctx, MusicFocusable focusable) { mAM = (AudioManager) ctx.getSystemService(Context.AUDIO_SERVICE); mFocusable = focusable; } /** Requests audio focus. Returns whether request was successful or not. */ public boolean requestFocus() { return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAM.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); } /** Abandons audio focus. Returns whether request was successful or not. */ public boolean abandonFocus() { return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == mAM.abandonAudioFocus(this); } /** * Called by AudioManager on audio focus changes. We implement this by calling our * MusicFocusable appropriately to relay the message. */ @Override public void onAudioFocusChange(int focusChange) { if (mFocusable == null) return; switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: mFocusable.onGainedAudioFocus(); break; case AudioManager.AUDIOFOCUS_LOSS: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: mFocusable.onLostAudioFocus(false); break; case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: mFocusable.onLostAudioFocus(true); break; default: } } } //src\com\example\android\musicplayer\MainActivity.java package com.example.android.musicplayer; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; public class MainActivity extends Activity implements OnClickListener { final String SUGGESTED_URL = "http://www.vorbis.com/music/Epoq-Lepidoptera.ogg"; Button mPlayButton; Button mPauseButton; Button mSkipButton; Button mRewindButton; Button mStopButton; Button mEjectButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mPlayButton = (Button) findViewById(R.id.playbutton); mPauseButton = (Button) findViewById(R.id.pausebutton); mSkipButton = (Button) findViewById(R.id.skipbutton); mRewindButton = (Button) findViewById(R.id.rewindbutton); mStopButton = (Button) findViewById(R.id.stopbutton); mEjectButton = (Button) findViewById(R.id.ejectbutton); mPlayButton.setOnClickListener(this); mPauseButton.setOnClickListener(this); mSkipButton.setOnClickListener(this); mRewindButton.setOnClickListener(this); mStopButton.setOnClickListener(this); mEjectButton.setOnClickListener(this); } @Override public void onClick(View target) { // Send the correct intent to the MusicService, according to the button that was clicked if (target == mPlayButton) startService(new Intent(MusicService.ACTION_PLAY)); else if (target == mPauseButton) startService(new Intent(MusicService.ACTION_PAUSE)); else if (target == mSkipButton) startService(new Intent(MusicService.ACTION_SKIP)); else if (target == mRewindButton) startService(new Intent(MusicService.ACTION_REWIND)); else if (target == mStopButton) startService(new Intent(MusicService.ACTION_STOP)); else if (target == mEjectButton) { showUrlDialog(); } } void showUrlDialog() { AlertDialog.Builder alertBuilder = new AlertDialog.Builder(this); alertBuilder.setTitle("Manual Input"); alertBuilder.setMessage("Enter a URL (must be http://)"); final EditText input = new EditText(this); alertBuilder.setView(input); input.setText(SUGGESTED_URL); alertBuilder.setPositiveButton("Play!", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dlg, int whichButton) { // Send an intent with the URL of the song to play. This is expected by // MusicService. Intent i = new Intent(MusicService.ACTION_URL); Uri uri = Uri.parse(input.getText().toString()); i.setData(uri); startService(i); } }); alertBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dlg, int whichButton) {} }); alertBuilder.show(); } } //src\com\example\android\musicplayer\MusicFocusable.java package com.example.android.musicplayer; public interface MusicFocusable { /** Signals that audio focus was gained. */ public void onGainedAudioFocus(); public void onLostAudioFocus(boolean canDuck); } //src\com\example\android\musicplayer\MusicIntentReceiver.java package com.example.android.musicplayer; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; /** * Receives broadcasted intents. In particular, we are interested in the * android.media.AUDIO_BECOMING_NOISY intent, which is broadcast, for example, when the user * disconnects the headphones. This class works because we are declaring it in a <receiver> * tag in AndroidManifest.xml. */ public class MusicIntentReceiver extends BroadcastReceiver { @Override public void onReceive(Context ctx, Intent intent) { if (intent.getAction().equals(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { Toast.makeText(ctx, "Headphones disconnected.", Toast.LENGTH_SHORT).show(); // send an intent to our MusicService to telling it to pause the audio ctx.startService(new Intent(MusicService.ACTION_PAUSE)); } } } //src\com\example\android\musicplayer\MusicRetriever.java package com.example.android.musicplayer; import java.util.ArrayList; import java.util.List; import java.util.Random; import android.content.ContentResolver; import android.content.ContentUris; import android.database.Cursor; import android.net.Uri; import android.util.Log; public class MusicRetriever { final String TAG = "MusicRetriever"; ContentResolver mContentResolver; // the items (songs) we have queried List<Item> mItems = new ArrayList<Item>(); Random mRandom = new Random(); public MusicRetriever(ContentResolver cr) { mContentResolver = cr; } /** * Loads music data. This method may take long, so be sure to call it asynchronously without * blocking the main thread. */ public void prepare() { Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Log.i(TAG, "Querying media..."); Log.i(TAG, "URI: " + uri.toString()); // Perform a query on the content resolver. The URI we're passing specifies that we // want to query for all audio media on external storage (e.g. SD card) Cursor cur = mContentResolver.query(uri, null, null, null, null); Log.i(TAG, "Query finished. " + (cur == null ? "Returned NULL." : "Returned a cursor.")); if (cur == null) { // Query failed... Log.e(TAG, "Failed to retrieve music: cursor is null :-("); return; } if (!cur.moveToFirst()) { // Nothing to query. There is no music on the device. How boring. Log.e(TAG, "Failed to move cursor to first row (no query results)."); return; } Log.i(TAG, "Listing..."); // retrieve the indices of the columns where the ID and title of the song are int titleColumn = cur.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE); int idColumn = cur.getColumnIndex(android.provider.MediaStore.Audio.Media._ID); Log.i(TAG, "Title column index: " + String.valueOf(titleColumn)); Log.i(TAG, "ID column index: " + String.valueOf(titleColumn)); // add each song to mItems do { Log.i(TAG, "ID: " + cur.getString(idColumn) + " Title: " + cur.getString(titleColumn)); mItems.add(new Item(cur.getLong(idColumn), cur.getString(titleColumn))); } while (cur.moveToNext()); Log.i(TAG, "Done querying media. MusicRetriever is ready."); } public ContentResolver getContentResolver() { return mContentResolver; } /** Returns a random Item. If there are no items available, returns null. */ public Item getRandomItem() { if (mItems.size() <= 0) return null; return mItems.get(mRandom.nextInt(mItems.size())); } public class Item { long id; String title; public Item(long id, String title) { this.id = id; this.title = title; } public long getId() { return id; } public String getTitle() { return title; } public Uri getURI() { return ContentUris.withAppendedId( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); } } } //src\com\example\android\musicplayer\MusicService.java package com.example.android.musicplayer; import java.io.IOException; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; import android.net.Uri; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; import android.os.IBinder; import android.os.PowerManager; import android.util.Log; import android.widget.Toast; public class MusicService extends Service implements OnCompletionListener, OnPreparedListener, OnErrorListener, MusicFocusable, PrepareMusicRetrieverTask.MusicRetrieverPreparedListener { NotificationManager mNotificationManager; // our media player MediaPlayer mPlayer = null; // our AudioFocusHelper object, if it's available (it's available on SDK level >= 8) // If not available, this will be null. Always check for null before using! AudioFocusHelper mAudioFocusHelper = null; // indicates the state our service: enum State { Retrieving, // the MediaRetriever is retrieving music Stopped, // media player is stopped and not prepared to play Preparing, // media player is preparing... Playing, // playback active (media player ready!). (but the media player may actually be // paused in this state if we don't have audio focus. But we stay in this state // so that we know we have to resume playback once we get focus back) Paused // playback paused (media player ready!) }; State mState = State.Retrieving; // if in Retrieving mode, this flag indicates whether we should start playing immediately // when we are ready or not. boolean mStartPlayingAfterRetrieve = false; // if mStartPlayingAfterRetrieve is true, this variable indicates the URL that we should // start playing when we are ready. If null, we should play a random song from the device Uri mWhatToPlayAfterRetrieve = null; enum PauseReason { UserRequest, // paused by user request FocusLoss, // paused because of audio focus loss }; // why did we pause? (only relevant if mState == State.Paused) PauseReason mPauseReason = PauseReason.UserRequest; // do we have audio focus? enum AudioFocus { NoFocusNoDuck, // we don't have audio focus, and can't duck NoFocusCanDuck, // we don't have focus, but can play at a low volume ("ducking") Focused // we have full audio focus } AudioFocus mAudioFocus = AudioFocus.NoFocusNoDuck; // title of the song we are currently playing String mSongTitle = ""; // whether the song we are playing is streaming from the network boolean mIsStreaming = false; // Wifi lock that we hold when streaming files from the internet, in order to prevent the // device from shutting off the Wifi radio WifiLock mWifiLock; // The tag we put on debug messages final static String TAG = "RandomMusicPlayer"; public static final String ACTION_PLAY = "com.example.android.musicplayer.action.PLAY"; public static final String ACTION_PAUSE = "com.example.android.musicplayer.action.PAUSE"; public static final String ACTION_STOP = "com.example.android.musicplayer.action.STOP"; public static final String ACTION_SKIP = "com.example.android.musicplayer.action.SKIP"; public static final String ACTION_REWIND = "com.example.android.musicplayer.action.REWIND"; public static final String ACTION_URL = "com.example.android.musicplayer.action.URL"; // The volume we set the media player to when we lose audio focus, but are allowed to reduce // the volume instead of stopping playback. public final float DUCK_VOLUME = 0.1f; final int NOTIFICATION_ID = 1; MusicRetriever mRetriever; Notification mNotification = null; void createMediaPlayerIfNeeded() { if (mPlayer == null) { mPlayer = new MediaPlayer(); mPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); mPlayer.setOnPreparedListener(this); mPlayer.setOnCompletionListener(this); mPlayer.setOnErrorListener(this); } else mPlayer.reset(); } @Override public void onCreate() { Log.i(TAG, "debug: Creating service"); // Create the Wifi lock (this does not acquire the lock, this just creates it) mWifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); // Create the retriever and start an asynchronous task that will prepare it. mRetriever = new MusicRetriever(getContentResolver()); (new PrepareMusicRetrieverTask(mRetriever,this)).execute(); // create the Audio Focus Helper, if the Audio Focus feature is available (SDK 8 or above) if (android.os.Build.VERSION.SDK_INT >= 8) mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this); else mAudioFocus = AudioFocus.Focused; // no focus feature, so we always "have" audio focus } @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent.getAction(); if (action.equals(ACTION_PLAY)) processPlayRequest(); else if (action.equals(ACTION_PAUSE)) processPauseRequest(); else if (action.equals(ACTION_SKIP)) processSkipRequest(); else if (action.equals(ACTION_STOP)) processStopRequest(); else if (action.equals(ACTION_REWIND)) processRewindRequest(); else if (action.equals(ACTION_URL)) processAddRequest(intent); return START_NOT_STICKY; // Means we started the service, but don't want it to // restart in case it's killed. } void processPlayRequest() { if (mState == State.Retrieving) { // If we are still retrieving media, just set the flag to start playing when we're // ready mWhatToPlayAfterRetrieve = null; // play a random song mStartPlayingAfterRetrieve = true; return; } tryToGetAudioFocus(); if (mState == State.Stopped) { // If we're stopped, just go ahead to the next song and start playing playNextSong(null); } else if (mState == State.Paused) { // If we're paused, just continue playback and restore the 'foreground service' state. mState = State.Playing; setUpAsForeground(mSongTitle + " (playing)"); configAndStartMediaPlayer(); } } void processPauseRequest() { if (mState == State.Retrieving) { // If we are still retrieving media, clear the flag that indicates we should start // playing when we're ready mStartPlayingAfterRetrieve = false; return; } if (mState == State.Playing) { // Pause media player and cancel the 'foreground service' state. mState = State.Paused; mPlayer.pause(); relaxResources(false); // while paused, we always retain the MediaPlayer giveUpAudioFocus(); } } void processRewindRequest() { if (mState == State.Playing || mState == State.Paused) mPlayer.seekTo(0); } void processSkipRequest() { if (mState == State.Playing || mState == State.Paused) { tryToGetAudioFocus(); playNextSong(null); } } void processStopRequest() { if (mState == State.Playing || mState == State.Paused) { mState = State.Stopped; // let go of all resources... relaxResources(true); giveUpAudioFocus(); // service is no longer necessary. Will be started again if needed. stopSelf(); } } void relaxResources(boolean releaseMediaPlayer) { // stop being a foreground service stopForeground(true); // stop and release the Media Player, if it's available if (releaseMediaPlayer && mPlayer != null) { mPlayer.reset(); mPlayer.release(); mPlayer = null; } // we can also release the Wifi lock, if we're holding it if (mWifiLock.isHeld()) mWifiLock.release(); } void giveUpAudioFocus() { if (mAudioFocus == AudioFocus.Focused && mAudioFocusHelper != null && mAudioFocusHelper.abandonFocus()) mAudioFocus = AudioFocus.NoFocusNoDuck; } void configAndStartMediaPlayer() { if (mAudioFocus == AudioFocus.NoFocusNoDuck) { if (mPlayer.isPlaying()) mPlayer.pause(); return; } else if (mAudioFocus == AudioFocus.NoFocusCanDuck) mPlayer.setVolume(DUCK_VOLUME, DUCK_VOLUME); // we'll be relatively quiet else mPlayer.setVolume(1.0f, 1.0f); // we can be loud if (!mPlayer.isPlaying()) mPlayer.start(); } void processAddRequest(Intent intent) { if (mState == State.Retrieving) { // we'll play the requested URL right after we finish retrieving mWhatToPlayAfterRetrieve = intent.getData(); mStartPlayingAfterRetrieve = true; } else if (mState == State.Playing || mState == State.Paused || mState == State.Stopped) { Log.i(TAG, "Playing from URL/path: " + intent.getData().toString()); tryToGetAudioFocus(); playNextSong(intent.getData().toString()); } } /** * Shortcut to making and displaying a toast. Seemed cleaner than repeating * this code everywhere, at least for this sample. */ void say(String message) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } void tryToGetAudioFocus() { if (mAudioFocus != AudioFocus.Focused && mAudioFocusHelper != null && mAudioFocusHelper.requestFocus()) mAudioFocus = AudioFocus.Focused; } void playNextSong(String manualUrl) { mState = State.Stopped; relaxResources(false); // release everything except MediaPlayer try { if (manualUrl != null) { // set the source of the media player to a manual URL or path createMediaPlayerIfNeeded(); mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mPlayer.setDataSource(manualUrl); mSongTitle = manualUrl; mIsStreaming = manualUrl.startsWith("http:") || manualUrl.startsWith("https:"); } else { mIsStreaming = false; // playing a locally available song MusicRetriever.Item item = mRetriever.getRandomItem(); if (item == null) { say("No song to play :-("); return; } // set the source of the media player a a content URI createMediaPlayerIfNeeded(); mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mPlayer.setDataSource(getApplicationContext(), item.getURI()); mSongTitle = item.getTitle(); } mState = State.Preparing; setUpAsForeground(mSongTitle + " (loading)"); mPlayer.prepareAsync(); if (mIsStreaming) mWifiLock.acquire(); else if (mWifiLock.isHeld()) mWifiLock.release(); } catch (IOException ex) { Log.e("MusicService", "IOException playing next song: " + ex.getMessage()); ex.printStackTrace(); } } /** Called when media player is done playing current song. */ @Override public void onCompletion(MediaPlayer player) { // The media player finished playing the current song, so we go ahead and start the next. playNextSong(null); } /** Called when media player is done preparing. */ @Override public void onPrepared(MediaPlayer player) { // The media player is done preparing. That means we can start playing! mState = State.Playing; updateNotification(mSongTitle + " (playing)"); configAndStartMediaPlayer(); } /** Updates the notification. */ void updateNotification(String text) { PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); mNotification.setLatestEventInfo(getApplicationContext(), "RandomMusicPlayer", text, pi); mNotificationManager.notify(NOTIFICATION_ID, mNotification); } void setUpAsForeground(String text) { PendingIntent pi = PendingIntent.getActivity(getApplicationContext(), 0, new Intent(getApplicationContext(), MainActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); mNotification = new Notification(); mNotification.tickerText = text; mNotification.icon = R.drawable.ic_stat_playing; mNotification.flags |= Notification.FLAG_ONGOING_EVENT; mNotification.setLatestEventInfo(getApplicationContext(), "RandomMusicPlayer", text, pi); startForeground(NOTIFICATION_ID, mNotification); } @Override public boolean onError(MediaPlayer mp, int what, int extra) { Toast.makeText(getApplicationContext(), "Media player error! Resetting.", Toast.LENGTH_SHORT).show(); Log.e(TAG, "Error: what=" + String.valueOf(what) + ", extra=" + String.valueOf(extra)); mState = State.Stopped; relaxResources(true); giveUpAudioFocus(); return true; // true indicates we handled the error } @Override public void onGainedAudioFocus() { Toast.makeText(getApplicationContext(), "gained audio focus.", Toast.LENGTH_SHORT).show(); mAudioFocus = AudioFocus.Focused; // restart media player with new focus settings if (mState == State.Playing) configAndStartMediaPlayer(); } @Override public void onLostAudioFocus(boolean canDuck) { Toast.makeText(getApplicationContext(), "lost audio focus." + (canDuck ? "can duck" : "no duck"), Toast.LENGTH_SHORT).show(); mAudioFocus = canDuck ? AudioFocus.NoFocusCanDuck : AudioFocus.NoFocusNoDuck; // start/restart/pause media player with new focus settings if (mPlayer != null && mPlayer.isPlaying()) configAndStartMediaPlayer(); } @Override public void onMusicRetrieverPrepared() { // Done retrieving! mState = State.Stopped; // If the flag indicates we should start playing after retrieving, let's do that now. if (mStartPlayingAfterRetrieve) { tryToGetAudioFocus(); playNextSong(mWhatToPlayAfterRetrieve == null ? null : mWhatToPlayAfterRetrieve.toString()); } } @Override public void onDestroy() { // Service is being killed, so make sure we release our resources mState = State.Stopped; relaxResources(true); giveUpAudioFocus(); } @Override public IBinder onBind(Intent arg0) { return null; } } //src\com\example\android\musicplayer\PrepareMusicRetrieverTask.java package com.example.android.musicplayer; import android.os.AsyncTask; public class PrepareMusicRetrieverTask extends AsyncTask<Void, Void, Void> { MusicRetriever mRetriever; MusicRetrieverPreparedListener mListener; public PrepareMusicRetrieverTask(MusicRetriever retriever, MusicRetrieverPreparedListener listener) { mRetriever = retriever; mListener = listener; } @Override protected Void doInBackground(Void... arg0) { mRetriever.prepare(); return null; } @Override protected void onPostExecute(Void result) { mListener.onMusicRetrieverPrepared(); } public interface MusicRetrieverPreparedListener { public void onMusicRetrieverPrepared(); } } // //res\layout-land\main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:background="#000040" > <TextView android:text="Random Music Player" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="20dp" android:textColor="#ffffff" android:textSize="20sp" android:textStyle="bold" /> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" > <Button android:id="@+id/rewindbutton" android:background="@drawable/selector_rew" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/playbutton" android:background="@drawable/selector_play" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/pausebutton" android:background="@drawable/selector_pause" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/skipbutton" android:background="@drawable/selector_ff" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/stopbutton" android:background="@drawable/selector_stop" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/ejectbutton" android:background="@drawable/selector_eject" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> </LinearLayout> </LinearLayout> // //res\layout-port\main.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" android:background="#000040" > <TextView android:text="Random Music Player" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="20dp" android:textColor="#ffffff" android:textSize="20sp" android:textStyle="bold" /> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:layout_margin="10dp" > <Button android:id="@+id/rewindbutton" android:background="@drawable/selector_rew" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/playbutton" android:background="@drawable/selector_play" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/pausebutton" android:background="@drawable/selector_pause" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/skipbutton" android:background="@drawable/selector_ff" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> </LinearLayout> <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:layout_margin="10dp" > <Button android:id="@+id/stopbutton" android:background="@drawable/selector_stop" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> <Button android:id="@+id/ejectbutton" android:background="@drawable/selector_eject" android:layout_width="64dp" android:layout_height="64dp" android:layout_margin="5dp" /> </LinearLayout> </LinearLayout>