• 截取p3片段


    一、布局 main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/player_bg2" >
    
        <com.hwtt.ringdemo.LyricView
            android:id="@+id/mylrc"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_marginBottom="100dip"
            android:layout_marginTop="20dip" />
    
        <RelativeLayout
            android:id="@+id/media_footer"
            android:layout_width="fill_parent"
            android:layout_height="80dip"
            android:layout_alignParentBottom="true"
            android:background="@drawable/mini_background" >
    
            <TextView
                android:id="@+id/start_tx"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dip"
                android:text="起始时间:" />
    
            <Button
                android:id="@+id/startbtn"
                android:layout_width="wrap_content"
                android:layout_height="30dip"
                android:layout_marginTop="10dip"
                android:layout_toRightOf="@id/start_tx"
                android:text="起始点"
                android:textSize="10dip" />
    
            <TextView
                android:id="@+id/starttime"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/start_tx"
                android:layout_marginLeft="10dip"
                android:text="00:00" />
    
            <TextView
                android:id="@+id/end_tx"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/starttime"
                android:text="结束时间:" />
    
            <TextView
                android:id="@+id/endtime"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_below="@id/end_tx"
                android:layout_marginLeft="10dip"
                android:text="00:00" />
    
            <ImageView
                android:id="@+id/media_next"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentBottom="true"
                android:layout_alignParentRight="true"
                android:layout_marginBottom="10dip"
                android:src="@drawable/mini_next_button" />
    
            <ImageView
                android:id="@+id/media_start"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignTop="@+id/media_next"
                android:layout_toLeftOf="@+id/media_next"
                android:src="@drawable/mini_play_button" />
    
            <ImageView
                android:id="@+id/media_last"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignTop="@+id/media_start"
                android:layout_marginRight="10dip"
                android:layout_toLeftOf="@+id/media_start"
                android:src="@drawable/mini_pre_button" />
    
            <ImageView
                android:id="@+id/save_img"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@+id/media_last"
                android:layout_toLeftOf="@+id/media_last"
                android:src="@drawable/menu_save" />
    
            <Button
                android:id="@+id/endbtn"
                android:layout_width="wrap_content"
                android:layout_height="30dip"
                android:layout_alignBaseline="@+id/end_tx"
                android:layout_alignBottom="@+id/end_tx"
                android:layout_alignLeft="@+id/startbtn"
                android:text="结束点"
                android:textSize="10dip" />
        </RelativeLayout>
    
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_above="@id/media_footer" >
    
            <SeekBar
                android:id="@+id/ring_seekbar"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dip"
                android:layout_marginRight="10dip" />
        </RelativeLayout>
    
    </RelativeLayout>

    file_save.xml

    <?xml version="1.0" encoding="utf-8"?>
    <!--
    /**
     * Copyright (c) 2007, Google Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License"); 
     * you may not use this file except in compliance with the License. 
     * You may obtain a copy of the License at 
     *
     *     http://www.apache.org/licenses/LICENSE-2.0 
     *
     * Unless required by applicable law or agreed to in writing, software 
     * distributed under the License is distributed on an "AS IS" BASIS, 
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
     * See the License for the specific language governing permissions and 
     * limitations under the License.
     */
    -->
    
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="300sp"
        android:layout_height="wrap_content"
        android:layout_marginBottom="10dip"
        android:layout_marginRight="10dip"
        android:orientation="vertical">
    
        <TextView
           android:text="@string/ringtone_type_label"
           android:textColor="#ffffffff"
           android:textSize="12sp"
           android:layout_marginLeft="15dip"
           android:layout_width="wrap_content" 
           android:layout_height="wrap_content" />
    
        <Spinner android:id="@+id/ringtone_type"
           android:layout_marginLeft="10dip"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content" />
    
        <TextView
           android:text="@string/ringtone_name_label"
           android:textColor="#ffffffff"
           android:textSize="12sp"
           android:layout_marginLeft="15dip"
           android:layout_width="wrap_content" 
           android:layout_height="wrap_content" />
    
        <EditText android:id="@+id/filename"
           android:layout_marginLeft="10dip"
           android:layout_width="280sp" 
           android:layout_height="wrap_content" />
    
        <LinearLayout
           android:layout_marginTop="5dip"
           android:layout_marginLeft="10dip"
           android:layout_width="fill_parent" 
           android:layout_height="wrap_content"
           android:gravity="center_horizontal">
    
          <Button android:id="@+id/save"
             android:text="@string/file_save_button_save"
             android:layout_width="100sp" 
             android:layout_height="wrap_content"
             android:layout_marginBottom="10dip" />
    
          <Button android:id="@+id/cancel"
             android:text="@string/file_save_button_cancel"
             android:layout_width="100sp" 
             android:layout_height="wrap_content"
             android:layout_marginBottom="10dip" />
    
        </LinearLayout>
    
    </LinearLayout>

    MainActivity

    package com.hwtt.ringdemo;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    import java.text.Format;
    import java.text.SimpleDateFormat;
    
    import android.app.Activity;
    import android.app.AlertDialog;
    import android.app.ProgressDialog;
    import android.app.AlertDialog.Builder;
    import android.content.ContentValues;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.media.MediaPlayer;
    import android.net.Uri;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;
    import android.provider.MediaStore;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.ImageView;
    import android.widget.SeekBar;
    import android.widget.TextView;
    import android.widget.Toast;
    import android.widget.SeekBar.OnSeekBarChangeListener;
    
    import com.hwtt.soundfile.CheapSoundFile;
    import com.hwtt.utils.FileSaveDialog;
    import com.hwtt.utils.FileUtils;
    
    public class RingdemoActivity extends Activity {
        /** Called when the activity is first created. */
        private ImageView miniStart;// 播放键
        private MediaPlayer mediaPlayer;
        private LyricView lyricView; // 显示歌词
        private SeekBar seekBar; // 进度条
        private int INTERVAL = 45;// 歌词每行的间隔
        private File mFile;
        private CheapSoundFile mSoundFile;
        private TextView startTime, endTime;
        private Button startBtn, endBtn;
        private ImageView save;
        private int mNewFileKind;
        private String mExtension;
        private ProgressDialog mProgressDialog;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            mediaPlayer = new MediaPlayer();
            playMusic(R.raw.smile);
            mFile = saveToneToSD(getResources().openRawResource(R.raw.smile), "voa");
            LoadToneFromSD();
            addView();
            init();
            setListeners();
            new Thread(new runable()).start();
        }
    
        /**
         * 初始化控件
         */
        private void addView() {
            // TODO Auto-generated method stub
            miniStart = (ImageView) findViewById(R.id.media_start);
            lyricView = (LyricView) findViewById(R.id.mylrc);
            seekBar = (SeekBar) findViewById(R.id.ring_seekbar);
            startTime = (TextView) findViewById(R.id.starttime);
            endTime = (TextView) findViewById(R.id.endtime);
            startBtn = (Button) findViewById(R.id.startbtn);
            endBtn = (Button) findViewById(R.id.endbtn);
            save = (ImageView) findViewById(R.id.save_img);
            save.setOnClickListener(new MyClickListener());
            startBtn.setOnClickListener(new MyClickListener());
            endBtn.setOnClickListener(new MyClickListener());
        }
    
        /**
         * 初始化歌词
         */
        private void init() {
            SerchLrc();
        }
        
    
    
        private void setListeners() {
            miniStart.setOnClickListener(new MyClickListener());
            seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
    
                @Override
                public void onStopTrackingTouch(SeekBar seekBar) {
                    // TODO Auto-generated method stub
    
                }
    
                @Override
                public void onStartTrackingTouch(SeekBar seekBar) {
                    // TODO Auto-generated method stub
    
                }
    
                @Override
                public void onProgressChanged(SeekBar seekBar, int progress,
                        boolean fromUser) {
                    // TODO Auto-generated method stub
                    if (fromUser) {
                        mediaPlayer.seekTo(progress);
                        lyricView.setOffsetY(220 - lyricView.SelectIndex(progress)
                                * (lyricView.getSIZEWORD() + INTERVAL - 1));
                    }
    
                    endTime.setText(mediaPlayer.getCurrentPosition() + "");
                }
            });
    //        mediaPlayer
    //                .setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
    //                    @Override
    //                    public void onCompletion(MediaPlayer mp) {
    //                        lyricView.SetTextSize();
    //                        lyricView.setOffsetY(200);
    //                        //mediaPlayer.start();
    //                    }
    //                });
            
        }
    
        public void SerchLrc() {
            LyricView.setBlLrc(true);
            LyricView.read();
            lyricView.SetTextSize();
            lyricView.setOffsetY(350);
        }
    
        class MyClickListener implements OnClickListener {
    
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                switch (v.getId()) {
                // 开始播放按钮监听
                case R.id.media_start:
                    if (mediaPlayer.isPlaying()) {
                        /* 暂停 */
                        mediaPlayer.pause();
                        miniStart.setImageResource(R.drawable.mini_pause_button);
                    } else {
                        /* 开始播放 */
                        mediaPlayer.start();
                        miniStart.setImageResource(R.drawable.mini_play_button);
                        lyricView.setOffsetY(220
                                - lyricView.SelectIndex(mediaPlayer
                                        .getCurrentPosition())
                                * (lyricView.getSIZEWORD() + INTERVAL - 1));
                        seekBar.setMax(mediaPlayer.getDuration());
                    }
                    break;
                case R.id.save_img:
                    toSaveTone();
                    break;
                case R.id.startbtn:
                    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                        startTime.setText(mediaPlayer.getCurrentPosition() + "");
                    }
                    break;
                case R.id.endbtn:
    
                    endTime.setText(mediaPlayer.getCurrentPosition() + "");
                    if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                        mediaPlayer.pause();
                        miniStart.setImageResource(R.drawable.mini_pause_button);
                    }
    
                    break;
                default:
    
                    break;
                }
    
            }
    
            private void toSaveTone() {
                // TODO Auto-generated method stub
                if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();
                    miniStart.setImageResource(R.drawable.mini_pause_button);
                }
                final Handler handler = new Handler() {
                    public void handleMessage(Message response) {
                        CharSequence newTitle = (CharSequence) response.obj;
                        mNewFileKind = response.arg1;
                        saveRingtone(newTitle);
                    }
                };
                Message message = Message.obtain(handler);
                FileSaveDialog dlog = new FileSaveDialog(RingdemoActivity.this,
                        getResources(), "ring", message);
                dlog.show();
    
            }
    
        }
    
        protected void saveRingtone(final CharSequence title) {
            // TODO Auto-generated method stub
            final String outPath = makeRingtoneFilename(title, mExtension);
            System.out.println("outPath-->" + outPath);
            Double endtime = 0.0;
            Double starttime = 0.0;
            
            if (!startTime.getText().toString().equals("00:00")) {
                starttime = Double.valueOf(startTime.getText().toString()) / 1000;
            }
            if(endTime.getText().toString().equals("00:00")){
                Toast.makeText(RingdemoActivity.this, "结束时间不能为0", 1).show();
                return;
            }
            endtime = Double.valueOf(endTime.getText().toString()) / 1000;
            System.out.println("endtime-->"+endtime);
            System.out.println("starttime-->"+starttime);
            final int startFrame = secondsToFrames(starttime);
            final int endFrame = secondsToFrames(endtime);
            final int duration = (int) (endtime - starttime + 0.5);
            System.out.println("mSoundFile.getSampleRate-->"
                    + mSoundFile.getSampleRate());
            System.out.println("mSoundFile.mSamplesPerFrame-->"
                    + mSoundFile.getSamplesPerFrame());
            System.out.println("开始时间:" + startFrame + "  结束时间:" + endFrame
                    + "   总时间:" + duration);
            mProgressDialog = new ProgressDialog(this);
            mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
            mProgressDialog.setTitle(R.string.progress_dialog_saving);
            mProgressDialog.setIndeterminate(true);
            mProgressDialog.setCancelable(false);
            mProgressDialog.show();
            new Thread() {
                public void run() {
                    final File outFile = new File(outPath);
                    try {
                        mSoundFile.WriteFile(outFile, startFrame, endFrame
                                - startFrame);
    
                        // Try to load the new file to make sure it worked
                        final CheapSoundFile.ProgressListener listener = new CheapSoundFile.ProgressListener() {
                            public boolean reportProgress(double frac) {
                                // Do nothing - we're not going to try to
                                // estimate when reloading a saved sound
                                // since it's usually fast, but hard to
                                // estimate anyway.
                                // System.out.println("listener-->rt__true");
                                return true; // Keep going
                            }
                        };
                        CheapSoundFile.create(outPath, listener);
                        System.out.println("create-->ok");
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
    
                    mProgressDialog.dismiss();
                    
                    Runnable runnable = new Runnable() {
                        public void run() {
                            System.out.println("title-->" + title);
                            System.out.println("outPath-->" + outPath);
                            System.out.println("outFile-->" + outFile);
                            System.out.println("duration-->" + duration);
                            Toast.makeText(RingdemoActivity.this, "保存在:"+outPath, 1).show();
                            afterSavingRingtone(title, outPath, outFile, duration);
                        }
    
                    };
                    mHandler.post(runnable);
    
                }
            }.start();
        }
    
        private void afterSavingRingtone(CharSequence title, final String outPath,
                File outFile, int duration) {
            // TODO Auto-generated method stub
            long length = outFile.length();
            if (length <= 512) {
                outFile.delete();
                new AlertDialog.Builder(this)
                        .setTitle(R.string.alert_title_failure).setMessage(
                                R.string.too_small_error).setPositiveButton(
                                R.string.alert_ok_button, null)
                        .setCancelable(false).show();
                return;
            }
    
            long fileSize = outFile.length();
            String mimeType = "audio/mpeg";
    
            String artist = "" + getResources().getText(R.string.artist_name);
    
            ContentValues values = new ContentValues();
            values.put(MediaStore.MediaColumns.DATA, outPath);
            values.put(MediaStore.MediaColumns.TITLE, title.toString());
            values.put(MediaStore.MediaColumns.SIZE, fileSize);
            values.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
    
            values.put(MediaStore.Audio.Media.ARTIST, artist);
            values.put(MediaStore.Audio.Media.DURATION, duration);
    
            values.put(MediaStore.Audio.Media.IS_RINGTONE,
                    mNewFileKind == FileSaveDialog.FILE_KIND_RINGTONE);
            values.put(MediaStore.Audio.Media.IS_NOTIFICATION,
                    mNewFileKind == FileSaveDialog.FILE_KIND_NOTIFICATION);
            values.put(MediaStore.Audio.Media.IS_ALARM,
                    mNewFileKind == FileSaveDialog.FILE_KIND_ALARM);
            values.put(MediaStore.Audio.Media.IS_MUSIC,
                    mNewFileKind == FileSaveDialog.FILE_KIND_MUSIC);
    
            // Insert it into the database
            Uri uri = MediaStore.Audio.Media.getContentUriForPath(outPath);
            final Uri newUri = getContentResolver().insert(uri, values);
            setResult(RESULT_OK, new Intent().setData(newUri));
            
            
            new AlertDialog.Builder(this).setTitle(R.string.alert_title_success)
                    .setMessage(R.string.alert_okcut_button).setPositiveButton(
                            R.string.alert_ok_trylisten,
                            new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog,
                                        int which) {
                                    // TODO Auto-generated method stub
    
                                    try {
                                        lyricView.setBlLrc(false);
                                        lyricView.invalidate();
                                        /* 重置MediaPlayer */
                                        mediaPlayer.reset();
                                        /* 设置要播放的文件的路径 */
                                        mediaPlayer.setDataSource(outPath);
                                        /* 准备播放 */
                                        mediaPlayer.prepare();
                                        /* 开始播放 */
                                        mediaPlayer.start();
                                        seekBar.setProgress(0);
                                        seekBar.setMax(mediaPlayer.getDuration());
                                        System.out.println("mediaPlayer.getDuration()"+mediaPlayer.getDuration());
                                    } catch (Exception e) {
                                        // TODO: handle exception
                                    }
    
                                }
                            }).setNegativeButton(R.string.alert_no_button, null)
                    .setCancelable(false).show();
    
        }
    
        public int secondsToFrames(double seconds) {
            return (int) (1.0 * seconds * mSoundFile.getSampleRate()
                    / mSoundFile.getSamplesPerFrame() + 0.5);
        }
    
        private String makeRingtoneFilename(CharSequence title, String extension) {
            String parentdir = "/sdcard/media/audio/music";
            File parentDirFile = new File(parentdir);
            parentDirFile.mkdirs();
            // If we can't write to that special path, try just writing
            // directly to the sdcard
            if (!parentDirFile.isDirectory()) {
                parentdir = "/sdcard";
            }
    
            // Turn the title into a filename
            String filename = "";
            for (int i = 0; i < title.length(); i++) {
                if (Character.isLetterOrDigit(title.charAt(i))) {
                    filename += title.charAt(i);
                }
            }
    
            // Try to make the filename unique
            String path = null;
            for (int i = 0; i < 100; i++) {
                String testPath;
                if (i > 0)
                    testPath = parentdir + "/" + filename + i + extension;
                else
                    testPath = parentdir + "/" + filename + extension;
    
                try {
                    RandomAccessFile f = new RandomAccessFile(new File(testPath),
                            "r");
                } catch (Exception e) {
                    // Good, the file didn't exist
                    path = testPath;
                    break;
                }
            }
            return path;
        }
    
        private String getExtensionFromFilename(String filename) {
            return filename.substring(filename.lastIndexOf('.'), filename.length());
        }
    
        class runable implements Runnable {
    
            @Override
            public void run() {
                // TODO Auto-generated method stub
                while (true) {
    
                    try {
                        Thread.sleep(100);
                        if (mediaPlayer.isPlaying()) {
                            lyricView.setOffsetY(lyricView.getOffsetY()
                                    - lyricView.SpeedLrc());
                            lyricView.SelectIndex(mediaPlayer.getCurrentPosition());
                            seekBar.setProgress(mediaPlayer.getCurrentPosition());
                            mHandler.post(mUpdateResults);
                        }
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    
        Handler mHandler = new Handler();
        Runnable mUpdateResults = new Runnable() {
            public void run() {
                lyricView.invalidate(); // 更新视图
            }
        };
    
        private void playMusic(int path) {
            // TODO Auto-generated method stub
    
            try {
                mediaPlayer = new MediaPlayer();
                // 重置多媒体
                mediaPlayer.reset();
                mediaPlayer = MediaPlayer.create(RingdemoActivity.this, path);
                // 准备播放
                if (mediaPlayer != null) {
                    mediaPlayer.stop();
                }
                mediaPlayer.prepare();
            } catch (Exception e) {
                // TODO: handle exception
            }
        }
    
        private File saveToneToSD(InputStream inputStream, String path) {
            File file = null;
            FileUtils fileUtils = new FileUtils();
            file = fileUtils.write2SDFromInput(path, "smile.mp3", inputStream);
    
            System.out.println("file-->" + file);
    
            return file;
    
        }
    
        private void LoadToneFromSD() {
            mExtension = getExtensionFromFilename(mFile.getAbsolutePath());
            System.out.println("mExtension-->" + mExtension);
            final CheapSoundFile.ProgressListener listener = new CheapSoundFile.ProgressListener() {
                public boolean reportProgress(double fractionComplete) {
    
                    long now = System.currentTimeMillis();
    
                    return true;
                }
            };
            // mSoundFile.create(fileName, progressListener);
            System.out.println("mFile-->" + mFile.getAbsolutePath());
            // mExtension = getExtensionFromFilename(mFile);
            try {
                mSoundFile = CheapSoundFile.create(mFile.getAbsolutePath(),
                        listener);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
    }

    LyricView

    package com.hwtt.ringdemo;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.Iterator;
    import java.util.TreeMap;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    import android.R.integer;
    import android.content.Context;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    
    public class LyricView extends View{
        private static Context context;
        private static TreeMap<Integer, LyricObject> lrc_map;  
        private float mX;       //屏幕X轴的中点,此值固定,保持歌词在X中间显示  
        private float offsetY;      //歌词在Y轴上的偏移量,此值会根据歌词的滚动变小  
        private static boolean blLrc=false;  
        private float touchY;   //当触摸歌词View时,保存为当前触点的Y轴坐标  
        private float touchX;  
        private boolean blScrollView=false;  
        private int lrcIndex=0; //保存歌词TreeMap的下标  
        private  int SIZEWORD=0;//显示歌词文字的大小值  
        private  int INTERVAL=45;//歌词每行的间隔  
        Paint paint=new Paint();//画笔,用于画不是高亮的歌词  
        Paint paintHL=new Paint();  //画笔,用于画高亮的歌词,即当前唱到这句歌词  
    
        public LyricView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            this.context = context;
            init();
            // TODO Auto-generated constructor stub
        }
    
        public LyricView(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.context = context;
            init();
            // TODO Auto-generated constructor stub
        }
    
        public LyricView(Context context) {
            super(context);
            this.context = context;
            init();
            // TODO Auto-generated constructor stub
        }
        
        public void init(){
            lrc_map = new TreeMap<Integer, LyricObject>();  
            offsetY=320;      
              
            paint=new Paint();  
            paint.setTextAlign(Paint.Align.CENTER);  
            paint.setColor(Color.YELLOW);  
            paint.setAntiAlias(true);  
            paint.setDither(true);  
            paint.setAlpha(180);  
              
              
            paintHL=new Paint();  
            paintHL.setTextAlign(Paint.Align.CENTER);  
              
            paintHL.setColor(Color.RED);  
            paintHL.setAntiAlias(true);  
            paintHL.setAlpha(255); 
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            // TODO Auto-generated method stub
             
            if(blLrc){  
                paintHL.setTextSize(SIZEWORD);  
                paint.setTextSize(SIZEWORD);  
                LyricObject temp=lrc_map.get(lrcIndex);  
                canvas.drawText(temp.lrc, mX, offsetY+(SIZEWORD+INTERVAL)*lrcIndex, paintHL);  
                // 画当前歌词之前的歌词  
                for(int i=lrcIndex-1;i>=0;i--){  
                    temp=lrc_map.get(i);  
                    if(offsetY+(SIZEWORD+INTERVAL)*i<0){  
                        break;  
                    }  
                    canvas.drawText(temp.lrc, mX, offsetY+(SIZEWORD+INTERVAL)*i, paint);  
                }  
                // 画当前歌词之后的歌词  
                for(int i=lrcIndex+1;i<lrc_map.size();i++){  
                    temp=lrc_map.get(i);  
                    if(offsetY+(SIZEWORD+INTERVAL)*i>600){  
                        break;  
                    }  
                    canvas.drawText(temp.lrc, mX, offsetY+(SIZEWORD+INTERVAL)*i, paint);  
                }  
            }  
            else{  
                paint.setTextSize(25);  
                canvas.drawText("找不到歌词", mX, 310, paint);  
            }  
            super.onDraw(canvas);  
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            // TODO Auto-generated method stub
             System.out.println("bllll==="+blScrollView);  
             float tt = event.getY();
             switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                touchX = event.getX();
                break;
            case MotionEvent.ACTION_MOVE:
                touchY = tt - touchY;
                offsetY = offsetY + touchY;
                break;
            case MotionEvent.ACTION_UP:
                blScrollView=false;  
                break;
    
            default:
                break;
            }
             touchY=tt;
            return true;
        }
        
         /** 
         * 根据歌词里面最长的那句来确定歌词字体的大小 
         */  
          
        public void SetTextSize(){  
            if(!blLrc){  
                return;  
            }  
            int max=lrc_map.get(0).lrc.length();  
            for(int i=1;i<lrc_map.size();i++){  
                LyricObject lrcStrLength=lrc_map.get(i);  
                if(max<lrcStrLength.lrc.length()){  
                    max=lrcStrLength.lrc.length();  
                }  
            }  
            SIZEWORD=320/max;  
          
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            // TODO Auto-generated method stub
             mX = w * 0.5f; 
            super.onSizeChanged(w, h, oldw, oldh);
        }  
        
        /** 
         *  歌词滚动的速度 
         *  
         * @return 返回歌词滚动的速度 
         */  
        public Float SpeedLrc(){  
            float speed=0;  
            if(offsetY+(SIZEWORD+INTERVAL)*lrcIndex>220){  
                speed=((offsetY+(SIZEWORD+INTERVAL)*lrcIndex-220)/20);  
      
            } else if(offsetY+(SIZEWORD+INTERVAL)*lrcIndex < 120){  
                Log.i("speed", "speed is too fast!!!");  
                speed = 0;  
            }  
    //      if(speed<0.2){  
    //          speed=0.2f;  
    //      }  
            return speed;  
        }  
        
        /** 
         * 按当前的歌曲的播放时间,从歌词里面获得那一句 
         * @param time 当前歌曲的播放时间 
         * @return 返回当前歌词的索引值 
         */ 
        
        public int SelectIndex(int time){  
            if(!blLrc){  
                return 0;  
            }  
            int index=0;  
            for(int i=0;i<lrc_map.size();i++){  
                LyricObject temp=lrc_map.get(i);  
                if(temp.begintime<time){  
                    ++index;  
                }  
            }  
            lrcIndex=index-1;  
            if(lrcIndex<0){  
                lrcIndex=0;  
            }  
            return lrcIndex;  
          
        }
        
        
    public static void setBlLrc(boolean blLrc) {
            LyricView.blLrc = blLrc;
        }
    
        //    
        /** 
         * 读取歌词文件 
         * @param file 歌词的路径 
         *  
         */  
        public static void read() {  
            TreeMap<Integer, LyricObject> lrc_read =new TreeMap<Integer, LyricObject>();  
            String data = "";  
            try {  
                if(!blLrc){
                    return;
                }
              blLrc=true;  
              
              InputStream myInput = context.getResources().openRawResource(R.raw.smilelrc);
              InputStreamReader reader = new InputStreamReader(myInput,"GB2312");
              BufferedReader br = new BufferedReader(reader);
              int i = 0;  
              Pattern pattern = Pattern.compile("\\d{2}");  
              while ((data = br.readLine()) != null) {     
                 // System.out.println("++++++++++++>>"+data);  
                    data = data.replace("[","");//将前面的替换成后面的  
                    data = data.replace("]","@");  
                    String splitdata[] =data.split("@");//分隔  
                    if(data.endsWith("@")){  
                        for(int k=0;k<splitdata.length;k++){  
                            String str=splitdata[k];  
                              
                            str = str.replace(":",".");  
                            str = str.replace(".","@");  
                            String timedata[] =str.split("@");  
                            Matcher matcher = pattern.matcher(timedata[0]);  
                            if(timedata.length==3 && matcher.matches()){  
                                int m = Integer.parseInt(timedata[0]);  //
                                int s = Integer.parseInt(timedata[1]);  //
                                int ms = Integer.parseInt(timedata[2]); //毫秒  
                                int currTime = (m*60+s)*1000+ms*10;  
                                LyricObject item1= new LyricObject();  
                                item1.begintime = currTime;  
                                item1.lrc       = "";  
                                lrc_read.put(currTime,item1);  
                            }  
                        }  
                          
                    }  
                    else{  
                        String lrcContenet = splitdata[splitdata.length-1];   
                  
                        for (int j=0;j<splitdata.length-1;j++)  
                        {  
                            String tmpstr = splitdata[j];  
                              
                            tmpstr = tmpstr.replace(":",".");  
                            tmpstr = tmpstr.replace(".","@");  
                            String timedata[] =tmpstr.split("@");  
                            Matcher matcher = pattern.matcher(timedata[0]);  
                            if(timedata.length==3 && matcher.matches()){  
                                int m = Integer.parseInt(timedata[0]);  //
                                int s = Integer.parseInt(timedata[1]);  //
                                int ms = Integer.parseInt(timedata[2]); //毫秒  
                                int currTime = (m*60+s)*1000+ms*10;  
                                LyricObject item1= new LyricObject();  
                                item1.begintime = currTime;  
                                item1.lrc       = lrcContenet;  
                                lrc_read.put(currTime,item1);// 将currTime当标签  item1当数据 插入TreeMap里  
                                i++;  
                            }  
                        }  
                    }  
                      
              }   
    //         stream.close();  
              myInput.close();
            }  
            catch (FileNotFoundException e) {  
            }  
            catch (IOException e) {  
            }  
              
            /* 
             * 遍历hashmap 计算每句歌词所需要的时间 
            */  
            lrc_map.clear();  
            data ="";  
            Iterator<Integer> iterator = lrc_read.keySet().iterator();  
            LyricObject oldval  = null;  
            int i =0;  
            while(iterator.hasNext()) {  
                Object ob =iterator.next();  
                  
                LyricObject val = (LyricObject)lrc_read.get(ob);  
                  
                if (oldval==null)  
                    oldval = val;  
                else  
                {  
                    LyricObject item1= new LyricObject();  
                    item1  = oldval;  
                    item1.timeline = val.begintime-oldval.begintime;  
                    lrc_map.put(new Integer(i), item1);  
                    i++;  
                    oldval = val;  
                }  
                if (!iterator.hasNext()) {  
                    lrc_map.put(new Integer(i), val);  
                }  
                  
            }  
      
        } 
        /** 
         * @return the blLrc 
         */  
        public static boolean isBlLrc() {  
            return blLrc;  
        }  
      
        /** 
         * @return the offsetY 
         */  
        public float getOffsetY() {  
            return offsetY;  
        }  
      
        /** 
         * @param offsetY the offsetY to set 
         */  
        public void setOffsetY(float offsetY) {  
            this.offsetY = offsetY;  
        }  
      
        /** 
         * @return 返回歌词文字的大小 
         */  
        public int getSIZEWORD() {  
            return SIZEWORD;  
        }  
      
        /** 
         * 设置歌词文字的大小 
         * @param sIZEWORD the sIZEWORD to set 
         */  
        public void setSIZEWORD(int sIZEWORD) {  
            SIZEWORD = sIZEWORD;  
        }
          
    
    }

    LyricObject

    package com.hwtt.ringdemo;
    
    public class LyricObject {
             public int begintime; // 开始时间  
            public int endtime; // 结束时间  
            public int timeline; // 单句歌词用时  
            public String lrc; // 单句歌词  
    }

    FileUtils

    package com.hwtt.utils;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    import android.os.Environment;
    
    public class FileUtils {
        // sd地址
        private String SDPATH;
        // 文件大小
        private int FILESIZE = 4 * 1024;
    
        public String getSDPATH() {
            return SDPATH;
        }
    
        public void setSDPATH(String sDPATH) {
            SDPATH = sDPATH;
        }
    
        public FileUtils() {
            // 得到当前外部存储设备的目录( /SDCARD )
            SDPATH = Environment.getExternalStorageDirectory() + "/";
        }
    
        /**
         * 在SD卡上创建文件
         * 
         * @param fileName
         *            创建的文件名称
         * @return
         * @throws IOException
         */
        public File createFile(String fileName) throws IOException {
            File file = new File(SDPATH+ fileName);
            file.createNewFile();
            return file;
        }
    
        /**
         * 在sd卡上创建目录
         * 
         * @param dirName
         * @return
         */
        public File createSDDir(String dirName) {
            File file = new File(SDPATH + dirName);
            return file;
        }
    
        /**
         * 判断目录是否存在
         */
        public boolean isFileExist(String fileName) {
            File file = new File(SDPATH + fileName);
            return file.exists();
        }
    
        public File write2SDFromInput(String path, String fileName,
                InputStream input) {
            File file = null;
            OutputStream output = null;
            //createSDDir(path);
            try {
                
                file = createFile(fileName);
                output = new FileOutputStream(file);
                byte[] buffer = new byte[FILESIZE];
                while ((input.read(buffer)) != -1) {
                    output.write(buffer);
                }
                output.flush();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                try {
                    output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    
            return file;
        }
    
    }

    FileSaveDialog

    /*
     * Copyright (C) 2008 Google Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.hwtt.utils;
    
    import android.app.Dialog;
    import android.content.Context;
    import android.content.res.Resources;
    import android.os.Message;
    import android.util.Log;
    import android.view.View;
    import android.widget.AdapterView;
    import android.widget.AdapterView.OnItemSelectedListener;
    import android.widget.ArrayAdapter;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Spinner;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    
    import com.hwtt.ringdemo.R;
    
    public class FileSaveDialog extends Dialog {
    
        // File kinds - these should correspond to the order in which
        // they're presented in the spinner control
        public static final int FILE_KIND_MUSIC = 0;
        public static final int FILE_KIND_ALARM = 1;
        public static final int FILE_KIND_NOTIFICATION = 2;
        public static final int FILE_KIND_RINGTONE = 3;
    
        private Spinner mTypeSpinner;
        private EditText mFilename;
        private Message mResponse;
        private String mOriginalName;
        private ArrayList<String> mTypeArray;
        private int mPreviousSelection;
    
        /**
         * Return a human-readable name for a kind (music, alarm, ringtone, ...).
         * These won't be displayed on-screen (just in logs) so they shouldn't
         * be translated.
         */
        public static String KindToName(int kind) {
            switch(kind) {
            default:
                return "Unknown";
            case FILE_KIND_MUSIC:
                return "Music";
            case FILE_KIND_ALARM:
                return "Alarm";
            case FILE_KIND_NOTIFICATION:
                return "Notification";
            case FILE_KIND_RINGTONE:
                return "Ringtone";
            }
        }
    
        public FileSaveDialog(Context context,
                              Resources resources,
                              String originalName,
                              Message response) {
            super(context);
    
            // Inflate our UI from its XML layout description.
            setContentView(R.layout.file_save);
    
            setTitle(resources.getString(R.string.file_save_title));
    
            mTypeArray = new ArrayList<String>();
            mTypeArray.add(resources.getString(R.string.type_music));
            mTypeArray.add(resources.getString(R.string.type_alarm));
            mTypeArray.add(resources.getString(R.string.type_notification));
            mTypeArray.add(resources.getString(R.string.type_ringtone));
    
            mFilename = (EditText)findViewById(R.id.filename);
            mOriginalName = originalName;
    
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(
                context, android.R.layout.simple_spinner_item, mTypeArray);
            adapter.setDropDownViewResource(
                android.R.layout.simple_spinner_dropdown_item);
            mTypeSpinner = (Spinner) findViewById(R.id.ringtone_type);
            mTypeSpinner.setAdapter(adapter);
            mTypeSpinner.setSelection(FILE_KIND_RINGTONE);
            mPreviousSelection = FILE_KIND_RINGTONE;
    
            setFilenameEditBoxFromName(false);
    
            mTypeSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {
                    public void onItemSelected(AdapterView parent,
                                               View v,
                                               int position,
                                               long id) {
                        setFilenameEditBoxFromName(true);
                    }
                    public void onNothingSelected(AdapterView parent) {
                    }
                });
    
            Button save = (Button)findViewById(R.id.save);
            save.setOnClickListener(saveListener);
            Button cancel = (Button)findViewById(R.id.cancel);
            cancel.setOnClickListener(cancelListener);
            mResponse = response;
        }
    
        private void setFilenameEditBoxFromName(boolean onlyIfNotEdited) {
            if (onlyIfNotEdited) {
                CharSequence currentText = mFilename.getText();
                String expectedText = mOriginalName + " " +
                    mTypeArray.get(mPreviousSelection);
    
                if (!expectedText.contentEquals(currentText)) {
                    return;
                }
            }
    
            int newSelection = mTypeSpinner.getSelectedItemPosition();
            String newSuffix = mTypeArray.get(newSelection);
            mFilename.setText(mOriginalName + " " + newSuffix);
            mPreviousSelection = mTypeSpinner.getSelectedItemPosition();
        }
    
        private View.OnClickListener saveListener = new View.OnClickListener() {
                public void onClick(View view) {
                    mResponse.obj = mFilename.getText();
                    mResponse.arg1 = mTypeSpinner.getSelectedItemPosition();
                    mResponse.sendToTarget();
                    dismiss();
                }
            };
    
        private View.OnClickListener cancelListener = new View.OnClickListener() {
                public void onClick(View view) {
                    dismiss();
                }
            };
    }

    soundfile->CheapAAC

    /*
     * Copyright (C) 2009 Google Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.hwtt.soundfile;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    
    import java.util.HashMap;
    
    /**
     * CheapAAC is a CheapSoundFile implementation for AAC (Advanced Audio
     * Codec) encoded sound files.  It supports files with an MP4 header,
     * including unencrypted files encoded by Apple iTunes, and also
     * files with a more basic ADTS header.
     */
    public class CheapAAC extends CheapSoundFile {
        public static Factory getFactory() {
            return new Factory() {
                public CheapSoundFile create() {
                    return new CheapAAC();
                }
                public String[] getSupportedExtensions() {
                    return new String[] { "aac", "m4a" };
                }
            };
        }
    
        class Atom {
            public int start;
            public int len;  // including header
            public byte[] data;
        };
    
        public static final int kDINF = 0x64696e66;
        public static final int kFTYP = 0x66747970;
        public static final int kHDLR = 0x68646c72;
        public static final int kMDAT = 0x6d646174;
        public static final int kMDHD = 0x6d646864;
        public static final int kMDIA = 0x6d646961;
        public static final int kMINF = 0x6d696e66;
        public static final int kMOOV = 0x6d6f6f76;
        public static final int kMP4A = 0x6d703461;
        public static final int kMVHD = 0x6d766864;
        public static final int kSMHD = 0x736d6864;
        public static final int kSTBL = 0x7374626c;
        public static final int kSTCO = 0x7374636f;
        public static final int kSTSC = 0x73747363;
        public static final int kSTSD = 0x73747364;
        public static final int kSTSZ = 0x7374737a;
        public static final int kSTTS = 0x73747473;
        public static final int kTKHD = 0x746b6864;
        public static final int kTRAK = 0x7472616b;
    
        public static final int[] kRequiredAtoms = {
            kDINF,
            kHDLR,
            kMDHD,
            kMDIA,
            kMINF,
            kMOOV,
            kMVHD,
            kSMHD,
            kSTBL,
            kSTSD,
            kSTSZ,
            kSTTS,
            kTKHD,
            kTRAK,
        };
    
        public static final int[] kSaveDataAtoms = {
            kDINF,
            kHDLR,
            kMDHD,
            kMVHD,
            kSMHD,
            kTKHD,
            kSTSD,
        };
    
        // Member variables containing frame info
        private int mNumFrames;
        private int[] mFrameOffsets;
        private int[] mFrameLens;
        private int[] mFrameGains;
        private int mFileSize;
        private HashMap<Integer, Atom> mAtomMap;
    
        // Member variables containing sound file info
        private int mBitrate;
        private int mSampleRate;
        private int mChannels;
        private int mSamplesPerFrame;
    
        // Member variables used only while initially parsing the file
        private int mOffset;
        private int mMinGain;
        private int mMaxGain;
        private int mMdatOffset;
        private int mMdatLength;
    
        public CheapAAC() {
        }
    
        public int getNumFrames() {
            return mNumFrames;
        }
    
        public int getSamplesPerFrame() {
            return mSamplesPerFrame;
        }
    
        public int[] getFrameOffsets() {
            return mFrameOffsets;
        }
    
        public int[] getFrameLens() {
            return mFrameLens;
        }
    
        public int[] getFrameGains() {
            return mFrameGains;
        }
    
        public int getFileSizeBytes() {
            return mFileSize;        
        }
    
        public int getAvgBitrateKbps() {
            return mFileSize / (mNumFrames * mSamplesPerFrame);
        }
    
        public int getSampleRate() {
            return mSampleRate;
        }
    
        public int getChannels() {
            return mChannels;
        }
    
        public String getFiletype() {
            return "AAC";
        }
    
        public String atomToString(int atomType) {
            String str = "";
            str += (char)((atomType >> 24) & 0xff);
            str += (char)((atomType >> 16) & 0xff);
            str += (char)((atomType >> 8) & 0xff);
            str += (char)(atomType & 0xff);
            return str;
        }
    
        public void ReadFile(File inputFile)
            throws java.io.FileNotFoundException,
                   java.io.IOException {
            super.ReadFile(inputFile);
            mChannels = 0;
            mSampleRate = 0;
            mBitrate = 0;
            mSamplesPerFrame = 0;
            mNumFrames = 0;
            mMinGain = 255;
            mMaxGain = 0;
            mOffset = 0;
            mMdatOffset = -1;
            mMdatLength = -1;
    
            mAtomMap = new HashMap<Integer, Atom>();
    
            // No need to handle filesizes larger than can fit in a 32-bit int
            mFileSize = (int)mInputFile.length();
    
            /*System.out.println("File size = " + mFileSize);*/
    
            if (mFileSize < 128) {
                throw new java.io.IOException("File too small to parse");
            }
    
            // Read the first 8 bytes
            FileInputStream stream = new FileInputStream(mInputFile);
            byte[] header = new byte[8];
            stream.read(header, 0, 8);
    
            if (header[0] == 0 &&
                header[4] == 'f' &&
                header[5] == 't' &&
                header[6] == 'y' &&
                header[7] == 'p') {
                // Create a new stream, reset to the beginning of the file
                stream = new FileInputStream(mInputFile);
                parseMp4(stream, mFileSize);
            } else {
                throw new java.io.IOException("Unknown file format");
            }
    
            if (mMdatOffset > 0 && mMdatLength > 0) {
                stream = new FileInputStream(mInputFile);
                stream.skip(mMdatOffset);
                mOffset = mMdatOffset;
                parseMdat(stream, mMdatLength);
            } else {
                throw new java.io.IOException("Didn't find mdat");
            }
    
            /*
            for (int i = 0; i < mNumFrames; i++) {
                System.out.println("Gain " + i + ": " + mFrameGains[i]);
                }*/
    
            /*
            System.out.println("Atoms found:");
            for (int atomType : mAtomMap.keySet()) {
                System.out.println("    " + atomToString(atomType));
                }*/
    
            boolean bad = false;
            for (int requiredAtomType : kRequiredAtoms) {
                if (!mAtomMap.containsKey(requiredAtomType)) {
                    System.out.println("Missing atom: " +
                                       atomToString(requiredAtomType));
                    bad = true;
                }
            }
    
            if (bad) {
                throw new java.io.IOException("Could not parse MP4 file");
            }
        }
    
        private void parseMp4(InputStream stream, int maxLen)
                throws java.io.IOException {
            /*System.out.println("parseMp4 maxLen = " + maxLen);*/
    
            while (maxLen > 8) {
                int initialOffset = mOffset;
    
                byte[] atomHeader = new byte[8];
                stream.read(atomHeader, 0, 8);
                int atomLen =
                    ((0xff & atomHeader[0]) << 24) |
                    ((0xff & atomHeader[1]) << 16) |
                    ((0xff & atomHeader[2]) << 8) |
                    ((0xff & atomHeader[3]));
                /*System.out.println("atomType = " +
                                   (char)atomHeader[4] +
                                   (char)atomHeader[5] +
                                   (char)atomHeader[6] +
                                   (char)atomHeader[7] +
                                   "  " +
                                   "offset = " + mOffset +
                                   "  " +
                                   "atomLen = " + atomLen);*/
                if (atomLen > maxLen)
                    atomLen = maxLen;
                int atomType = 
                    ((0xff & atomHeader[4]) << 24) |
                    ((0xff & atomHeader[5]) << 16) |
                    ((0xff & atomHeader[6]) << 8) |
                    ((0xff & atomHeader[7]));
    
                Atom atom = new Atom();
                atom.start = mOffset;
                atom.len = atomLen;
                mAtomMap.put(atomType, atom);
    
                mOffset += 8;
    
                if (atomType == kMOOV ||
                    atomType == kTRAK ||
                    atomType == kMDIA ||
                    atomType == kMINF ||
                    atomType == kSTBL) {
                    parseMp4(stream, atomLen);
                } else if (atomType == kSTSZ) {
                    parseStsz(stream, atomLen - 8);
                } else if (atomType == kSTTS) {
                    parseStts(stream, atomLen - 8);
                } else if (atomType == kMDAT) {
                    mMdatOffset = mOffset;
                    mMdatLength = atomLen - 8;
                } else {
                    for (int savedAtomType : kSaveDataAtoms) {
                        if (savedAtomType == atomType) {
                            byte[] data = new byte[atomLen - 8];
                            stream.read(data, 0, atomLen - 8);
                            mOffset += atomLen - 8;
                            mAtomMap.get(atomType).data = data;
                        }
                    }
                }
    
                if (atomType == kSTSD) {
                    parseMp4aFromStsd();
                }
    
                maxLen -= atomLen;
                int skipLen = atomLen - (mOffset - initialOffset);
                /*System.out.println("* atomLen: " + atomLen);*/
                /*System.out.println("* mOffset: " + mOffset);*/
                /*System.out.println("* initialOffset: " + initialOffset);*/
                /*System.out.println("*   diff: " + (mOffset - initialOffset));*/
                /*System.out.println("* skipLen: " + skipLen);*/
    
                if (skipLen < 0) {
                    throw new java.io.IOException(
                        "Went over by " + (-skipLen) + " bytes");
                }
                                   
                stream.skip(skipLen);
                mOffset += skipLen;
            }
        }
    
        void parseStts(InputStream stream, int maxLen)
            throws java.io.IOException {
            byte[] sttsData = new byte[16];
            stream.read(sttsData, 0, 16);
            mOffset += 16;
            mSamplesPerFrame =
                ((0xff & sttsData[12]) << 24) |
                ((0xff & sttsData[13]) << 16) |
                ((0xff & sttsData[14]) << 8) |
                ((0xff & sttsData[15]));
    
            /*System.out.println("STTS samples per frame: " + mSamplesPerFrame);*/
        }
    
        void parseStsz(InputStream stream, int maxLen)
            throws java.io.IOException {
            byte[] stszHeader = new byte[12];
            stream.read(stszHeader, 0, 12);
            mOffset += 12;
            mNumFrames =
                ((0xff & stszHeader[8]) << 24) |
                ((0xff & stszHeader[9]) << 16) |
                ((0xff & stszHeader[10]) << 8) |
                ((0xff & stszHeader[11]));
            /*System.out.println("mNumFrames = " + mNumFrames);*/
    
            mFrameOffsets = new int[mNumFrames];
            mFrameLens = new int[mNumFrames];
            mFrameGains = new int[mNumFrames];
            byte[] frameLenBytes = new byte[4 * mNumFrames];
            stream.read(frameLenBytes, 0, 4 * mNumFrames);
            mOffset += 4 * mNumFrames;
            for (int i = 0; i < mNumFrames; i++) {
                mFrameLens[i] =
                    ((0xff & frameLenBytes[4 * i + 0]) << 24) |
                    ((0xff & frameLenBytes[4 * i + 1]) << 16) |
                    ((0xff & frameLenBytes[4 * i + 2]) << 8) |
                    ((0xff & frameLenBytes[4 * i + 3]));
                /*System.out.println("FrameLen[" + i + "] = " + mFrameLens[i]);*/
            }
        }
    
        void parseMp4aFromStsd() {
            byte[] stsdData = mAtomMap.get(kSTSD).data;
            mChannels =
                ((0xff & stsdData[32]) << 8) |
                ((0xff & stsdData[33]));
            mSampleRate =
                ((0xff & stsdData[40]) << 8) |
                ((0xff & stsdData[41]));
    
            /*System.out.println("%% channels = " + mChannels + ", " +
              "sampleRate = " + mSampleRate);*/
        }
    
        void parseMdat(InputStream stream, int maxLen)
            throws java.io.IOException {
            /*System.out.println("***MDAT***");*/
            int initialOffset = mOffset;
            for (int i = 0; i < mNumFrames; i++) {
                mFrameOffsets[i] = mOffset;
                /*System.out.println("&&& start: " + (mOffset - initialOffset));*/
                /*System.out.println("&&& start + len: " +
                  (mOffset - initialOffset + mFrameLens[i]));*/
                /*System.out.println("&&& maxLen: " + maxLen);*/
    
                if (mOffset - initialOffset + mFrameLens[i] > maxLen - 8) {
                    mFrameGains[i] = 0;
                } else {
                    readFrameAndComputeGain(stream, i);
                }
                if (mFrameGains[i] < mMinGain)
                    mMinGain = mFrameGains[i];
                if (mFrameGains[i] > mMaxGain)
                    mMaxGain = mFrameGains[i];
    
                if (mProgressListener != null) {
                    boolean keepGoing = mProgressListener.reportProgress(
                        mOffset * 1.0 / mFileSize);
                    if (!keepGoing) {
                        break;
                    }
                }
            }
        }
    
        void readFrameAndComputeGain(InputStream stream, int frameIndex)
            throws java.io.IOException {
    
            if (mFrameLens[frameIndex] < 4) {
                mFrameGains[frameIndex] = 0;
                stream.skip(mFrameLens[frameIndex]);
                return;
            }
    
            int initialOffset = mOffset;
    
            byte[] data = new byte[4];
            stream.read(data, 0, 4);
            mOffset += 4;
    
            /*System.out.println(
                "Block " + frameIndex + ": " +
                data[0] + " " +
                data[1] + " " +
                data[2] + " " +
                data[3]);*/
    
            int idSynEle = (0xe0 & data[0]) >> 5;
            /*System.out.println("idSynEle = " + idSynEle);*/
    
            switch(idSynEle) {
            case 0:  // ID_SCE: mono
                int monoGain = ((0x01 & data[0]) << 7) | ((0xfe & data[1]) >> 1);
                /*System.out.println("monoGain = " + monoGain);*/
                mFrameGains[frameIndex] = monoGain;
                break;
            case 1:  // ID_CPE: stereo
                int windowSequence = (0x60 & data[1]) >> 5;
                /*System.out.println("windowSequence = " + windowSequence);*/
                int windowShape = (0x10 & data[1]) >> 4;
                /*System.out.println("windowShape = " + windowShape);*/
    
                int maxSfb;
                int scaleFactorGrouping;
                int maskPresent;
                int startBit;
    
                if (windowSequence == 2) {
                    maxSfb = 0x0f & data[1];
    
                    scaleFactorGrouping = (0xfe & data[2]) >> 1;
    
                    maskPresent =
                        ((0x01 & data[2]) << 1) |
                        ((0x80 & data[3]) >> 7);
    
                    startBit = 25;
                } else {
                    maxSfb =
                        ((0x0f & data[1]) << 2) |
                        ((0xc0 & data[2]) >> 6);
    
                    scaleFactorGrouping = -1;
    
                    maskPresent = (0x18 & data[2]) >> 3;
    
                    startBit = 21;
                }
    
                /*System.out.println("maxSfb = " + maxSfb);*/
                /*System.out.println("scaleFactorGrouping = " +
                  scaleFactorGrouping);*/
                /*System.out.println("maskPresent = " + maskPresent);*/
                /*System.out.println("startBit = " + startBit);*/
    
                if (maskPresent == 1) {
                    int sfgZeroBitCount = 0;
                    for (int b = 0; b < 7; b++) {
                        if ((scaleFactorGrouping & (1 << b)) == 0) {
                            /*System.out.println("  1 point for bit " + b +
                                               ": " + (1 << b) +
                                               ", " + (scaleFactorGrouping & (1 << b)));*/
                            sfgZeroBitCount++;
                        }
                    }
                    /*System.out.println("sfgZeroBitCount = " + sfgZeroBitCount);*/
    
                    int numWindowGroups = 1 + sfgZeroBitCount;
                    /*System.out.println("numWindowGroups = " + numWindowGroups);*/
    
                    int skip = maxSfb * numWindowGroups;
                    /*System.out.println("skip = " + skip);*/
    
                    startBit += skip;
                    /*System.out.println("new startBit = " + startBit);*/
                }
    
                // We may need to fill our buffer with more than the 4
                // bytes we've already read, here.
                int bytesNeeded = 1 + ((startBit + 7) / 8);
                byte[] oldData = data;
                data = new byte[bytesNeeded];
                data[0] = oldData[0];
                data[1] = oldData[1];
                data[2] = oldData[2];
                data[3] = oldData[3];
                stream.read(data, 4, bytesNeeded - 4);
                mOffset += (bytesNeeded - 4);
                /*System.out.println("bytesNeeded: " + bytesNeeded);*/
    
                int firstChannelGain = 0;
                for (int b = 0; b < 8; b++) {
                    int b0 = (b + startBit) / 8;
                    int b1 = 7 - ((b + startBit) % 8);
                    int add = (((1 << b1) & data[b0]) >> b1) << (7 - b);
                    /*System.out.println("Bit " + (b  + startBit) + " " +
                                       "b0 " + b0 + " " +
                                       "b1 " + b1 + " " +
                                       "add " + add);*/
                    firstChannelGain += add;
                }
                /*System.out.println("firstChannelGain = " + firstChannelGain);*/
    
                mFrameGains[frameIndex] = firstChannelGain;
                break;
    
            default:
                if (frameIndex > 0) {
                    mFrameGains[frameIndex] = mFrameGains[frameIndex - 1];
                } else {
                    mFrameGains[frameIndex] = 0;
                }
                /*System.out.println("Unhandled idSynEle");*/
                break;
            }
    
            int skip = mFrameLens[frameIndex] - (mOffset - initialOffset);
            /*System.out.println("frameLen = " + mFrameLens[frameIndex]);*/
            /*System.out.println("Skip = " + skip);*/
    
            stream.skip(skip);
            mOffset += skip;
        }
    
        public void StartAtom(FileOutputStream out, int atomType)
                throws java.io.IOException {
            byte[] atomHeader = new byte[8];
            int atomLen = mAtomMap.get(atomType).len;
            atomHeader[0] = (byte)((atomLen >> 24) & 0xff);
            atomHeader[1] = (byte)((atomLen >> 16) & 0xff);
            atomHeader[2] = (byte)((atomLen >> 8) & 0xff);
            atomHeader[3] = (byte)(atomLen & 0xff);
            atomHeader[4] = (byte)((atomType >> 24) & 0xff);
            atomHeader[5] = (byte)((atomType >> 16) & 0xff);
            atomHeader[6] = (byte)((atomType >> 8) & 0xff);
            atomHeader[7] = (byte)(atomType & 0xff);
            out.write(atomHeader, 0, 8);
        }
    
        public void WriteAtom(FileOutputStream out, int atomType)
                throws java.io.IOException {
            Atom atom = mAtomMap.get(atomType);
            StartAtom(out, atomType);
            out.write(atom.data, 0, atom.len - 8);
        }
    
        public void SetAtomData(int atomType, byte[] data) {
            Atom atom = mAtomMap.get(atomType);
            if (atom == null) {
                atom = new Atom();
                mAtomMap.put(atomType, atom);
            }
            atom.len = data.length + 8;
            atom.data = data;
        }
    
        public void WriteFile(File outputFile, int startFrame, int numFrames)
                throws java.io.IOException {
            outputFile.createNewFile();
            FileInputStream in = new FileInputStream(mInputFile);
            FileOutputStream out = new FileOutputStream(outputFile);
    
            SetAtomData(kFTYP, new byte[] {
                    'M', '4', 'A', ' ',
                    0, 0, 0, 0,
                    'M', '4', 'A', ' ',
                    'm', 'p', '4', '2',
                    'i', 's', 'o', 'm',
                    0, 0, 0, 0
                });
            
            SetAtomData(kSTTS, new byte[] {
                    0, 0, 0, 0,  // version / flags
                    0, 0, 0, 1,  // entry count
                    (byte) ((numFrames >> 24) & 0xff),
                    (byte) ((numFrames >> 16) & 0xff),
                    (byte) ((numFrames >> 8) & 0xff),
                    (byte) (numFrames & 0xff),
                    (byte) ((mSamplesPerFrame >> 24) & 0xff),
                    (byte) ((mSamplesPerFrame >> 16) & 0xff),
                    (byte) ((mSamplesPerFrame >> 8) & 0xff),
                    (byte) (mSamplesPerFrame & 0xff)
                });
    
            SetAtomData(kSTSC, new byte[] {
                    0, 0, 0, 0,  // version / flags
                    0, 0, 0, 1,  // entry count
                    0, 0, 0, 1,  // first chunk
                    (byte) ((numFrames >> 24) & 0xff),
                    (byte) ((numFrames >> 16) & 0xff),
                    (byte) ((numFrames >> 8) & 0xff),
                    (byte) (numFrames & 0xff),
                    0, 0, 0, 1  // Smaple desc index
                });
    
            byte[] stszData = new byte[12 + 4 * numFrames];
            stszData[8] = (byte)((numFrames >> 24) & 0xff);
            stszData[9] = (byte)((numFrames >> 16) & 0xff);
            stszData[10] = (byte)((numFrames >> 8) & 0xff);
            stszData[11] = (byte)(numFrames & 0xff);
            for (int i = 0; i < numFrames; i++) {
                stszData[12 + 4 * i] =
                    (byte)((mFrameLens[startFrame + i] >> 24) & 0xff);
                stszData[13 + 4 * i] =
                    (byte)((mFrameLens[startFrame + i] >> 16) & 0xff);
                stszData[14 + 4 * i] =
                    (byte)((mFrameLens[startFrame + i] >> 8) & 0xff);
                stszData[15 + 4 * i] =
                    (byte)(mFrameLens[startFrame + i] & 0xff);
            }
            SetAtomData(kSTSZ, stszData);
    
            int mdatOffset =
                144 +
                4 * numFrames +
                mAtomMap.get(kSTSD).len +
                mAtomMap.get(kSTSC).len +
                mAtomMap.get(kMVHD).len +
                mAtomMap.get(kTKHD).len +
                mAtomMap.get(kMDHD).len +
                mAtomMap.get(kHDLR).len +
                mAtomMap.get(kSMHD).len +
                mAtomMap.get(kDINF).len;
    
            /*System.out.println("Mdat offset: " + mdatOffset);*/
    
            SetAtomData(kSTCO, new byte[] {
                    0, 0, 0, 0,  // version / flags
                    0, 0, 0, 1,  // entry count
                    (byte) ((mdatOffset >> 24) & 0xff),
                    (byte) ((mdatOffset >> 16) & 0xff),
                    (byte) ((mdatOffset >> 8) & 0xff),
                    (byte) (mdatOffset & 0xff),
                });
    
            mAtomMap.get(kSTBL).len =
                8 +
                mAtomMap.get(kSTSD).len +
                mAtomMap.get(kSTTS).len +
                mAtomMap.get(kSTSC).len +
                mAtomMap.get(kSTSZ).len +
                mAtomMap.get(kSTCO).len;
    
            mAtomMap.get(kMINF).len =
                8 +
                mAtomMap.get(kDINF).len +
                mAtomMap.get(kSMHD).len +
                mAtomMap.get(kSTBL).len;
    
            mAtomMap.get(kMDIA).len =
                8 +
                mAtomMap.get(kMDHD).len +
                mAtomMap.get(kHDLR).len +
                mAtomMap.get(kMINF).len;
    
            mAtomMap.get(kTRAK).len =
                8 +
                mAtomMap.get(kTKHD).len +
                mAtomMap.get(kMDIA).len;
    
            mAtomMap.get(kMOOV).len =
                8 +
                mAtomMap.get(kMVHD).len +
                mAtomMap.get(kTRAK).len;
    
            int mdatLen = 8;
            for (int i = 0; i < numFrames; i++) {
                mdatLen += mFrameLens[startFrame + i];
            }
            mAtomMap.get(kMDAT).len = mdatLen;
    
            WriteAtom(out, kFTYP);
            StartAtom(out, kMOOV);
            {
                WriteAtom(out, kMVHD);
                StartAtom(out, kTRAK);
                {
                    WriteAtom(out, kTKHD);
                    StartAtom(out, kMDIA);
                    {
                        WriteAtom(out, kMDHD);
                        WriteAtom(out, kHDLR);
                        StartAtom(out, kMINF);
                        {
                            WriteAtom(out, kDINF);
                            WriteAtom(out, kSMHD);
                            StartAtom(out, kSTBL);
                            {
                                WriteAtom(out, kSTSD);
                                WriteAtom(out, kSTTS);
                                WriteAtom(out, kSTSC);
                                WriteAtom(out, kSTSZ);
                                WriteAtom(out, kSTCO);
                            }
                        }
                    }
                }
            }
            StartAtom(out, kMDAT);
    
            int maxFrameLen = 0;
            for (int i = 0; i < numFrames; i++) {
                if (mFrameLens[startFrame + i] > maxFrameLen)
                    maxFrameLen = mFrameLens[startFrame + i];
            }
            byte[] buffer = new byte[maxFrameLen];
            int pos = 0;
            for (int i = 0; i < numFrames; i++) {
                int skip = mFrameOffsets[startFrame + i] - pos;
                int len = mFrameLens[startFrame + i];
                if (skip < 0) {
                    continue;
                }
                if (skip > 0) {
                    in.skip(skip);
                    pos += skip;
                }
                in.read(buffer, 0, len);
                out.write(buffer, 0, len);
                pos += len;
            }
    
            in.close();
            out.close();
        }
    
        /** For debugging
        public static void main(String[] argv) throws Exception {
            File f = new File("");
            CheapAAC c = new CheapAAC();
            c.ReadFile(f);
            c.WriteFile(new File(""),
                        0, c.getNumFrames());
        } **/
    };

    CheapAMR

    /*
     * Copyright (C) 2008 Google Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.hwtt.soundfile;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    import java.lang.Math;
    
    /**
     * CheapAMR is a CheapSoundFile implementation for AMR (Adaptive Multi-Rate)
     * encoded sound files, which is one of the native formats supported by
     * Android's MediaRecorder library.  It supports files with a full 3GPP
     * header, and also files with only a basic AMR header.
     *
     * While there are 8 bitrates and several other frame types in AMR,
     * this implementation currently only supports frametype=1,
     * MR515, 10.3 kbits / sec, which is the format encoded on Android 1.0
     * phones.  In the future it may be necessary to support other bitrates.
     */
    public class CheapAMR extends CheapSoundFile {
        public static Factory getFactory() {
            return new Factory() {
                public CheapSoundFile create() {
                    return new CheapAMR();
                }
                public String[] getSupportedExtensions() {
                    return new String[] { "3gpp", "3gp", "amr" };
                }
            };
        }
    
        // Member variables containing frame info
        private int mNumFrames;
        private int[] mFrameOffsets;
        private int[] mFrameLens;
        private int[] mFrameGains;
        private int mFileSize;
        private int mBitRate;
    
        // Member variables used only while initially parsing the file
        private int mOffset;
        private int mMaxFrames;
        private int mMinGain;
        private int mMaxGain;
    
        public CheapAMR() {
        }
    
        public int getNumFrames() {
            return mNumFrames;
        }
    
        public int getSamplesPerFrame() {
            return 40;
        }
    
        public int[] getFrameOffsets() {
            return mFrameOffsets;
        }
    
        public int[] getFrameLens() {
            return mFrameLens;
        }
    
        public int[] getFrameGains() {
            return mFrameGains;
        }
    
        public int getFileSizeBytes() {
            return mFileSize;        
        }
    
        public int getAvgBitrateKbps() {
            return mBitRate;
        }
    
        public int getSampleRate() {
            return 8000;
        }
    
        public int getChannels() {
            return 1;
        }
    
        public String getFiletype() {
            return "AMR";
        }
    
        public void ReadFile(File inputFile)
                throws java.io.FileNotFoundException,
                java.io.IOException {
            super.ReadFile(inputFile);
            mNumFrames = 0;
            mMaxFrames = 64;  // This will grow as needed
            mFrameOffsets = new int[mMaxFrames];
            mFrameLens = new int[mMaxFrames];
            mFrameGains = new int[mMaxFrames];
            mMinGain = 1000000000;
            mMaxGain = 0;
            mBitRate = 10;
            mOffset = 0;
    
            // No need to handle filesizes larger than can fit in a 32-bit int
            mFileSize = (int)mInputFile.length();
    
            if (mFileSize < 128) {
                throw new java.io.IOException("File too small to parse");
            }
    
            FileInputStream stream = new FileInputStream(mInputFile);
            byte[] header = new byte[12];
            stream.read(header, 0, 6);
            mOffset += 6;
            if (header[0] == '#' &&
                header[1] == '!' &&
                header[2] == 'A' &&
                header[3] == 'M' &&
                header[4] == 'R' &&
                header[5] == '\n') {
                parseAMR(stream, mFileSize - 6);
            }
    
            stream.read(header, 6, 6);
            mOffset += 6;
    
            if (header[4] == 'f' &&
                header[5] == 't' &&
                header[6] == 'y' &&
                header[7] == 'p' &&
                header[8] == '3' &&
                header[9] == 'g' &&
                header[10] == 'p' &&
                header[11] == '4') {
    
                int boxLen =
                    ((0xff & header[0]) << 24) |
                    ((0xff & header[1]) << 16) |
                    ((0xff & header[2]) << 8) |
                    ((0xff & header[3]));
    
                if (boxLen >= 4 && boxLen <= mFileSize - 8) {
                    stream.skip(boxLen - 12);
                    mOffset += boxLen - 12;
                }
    
                parse3gpp(stream, mFileSize - boxLen);
            }
        }
    
        private void parse3gpp(InputStream stream, int maxLen)
                throws java.io.IOException {
            if (maxLen < 8)
                return;
    
            byte[] boxHeader = new byte[8];
            stream.read(boxHeader, 0, 8);
            mOffset += 8;
    
            int boxLen =
                ((0xff & boxHeader[0]) << 24) |
                ((0xff & boxHeader[1]) << 16) |
                ((0xff & boxHeader[2]) << 8) |
                ((0xff & boxHeader[3]));
    
            if (boxLen > maxLen || boxLen <= 0)
                return;
    
            if (boxHeader[4] == 'm' &&
                boxHeader[5] == 'd' &&
                boxHeader[6] == 'a' &&
                boxHeader[7] == 't') {
                parseAMR(stream, boxLen);
                return;
            }
    
        stream.skip(boxLen - 8);
        mOffset += (boxLen - 8);
    
            parse3gpp(stream, maxLen - boxLen);
        }
    
        void parseAMR(InputStream stream, int maxLen)
                throws java.io.IOException {
            int[] prevEner = new int[4];
            for (int i = 0; i < 4; i++) {
                prevEner[i] = 0;
            }
    
            int[] prevEnerMR122 = new int[4];
            for (int i = 0; i < 4; i++) {
                prevEnerMR122[i] = -2381;
            }
    
            int originalMaxLen = maxLen;
            int bytesTotal = 0;
            while (maxLen > 0) {
                int bytesConsumed = parseAMRFrame(stream, maxLen, prevEner);
                bytesTotal += bytesConsumed;
                maxLen -= bytesConsumed;
    
                if (mProgressListener != null) {
                    boolean keepGoing = mProgressListener.reportProgress(
                        bytesTotal * 1.0 / originalMaxLen);
                    if (!keepGoing) {
                        break;
                    }
                }
            }
        }
    
        int parseAMRFrame(InputStream stream, int maxLen, int[] prevEner)
                throws java.io.IOException {
            int frameOffset = mOffset;
            byte[] frameTypeHeader = new byte[1];
            stream.read(frameTypeHeader, 0, 1);
            mOffset += 1;
            int frameType = ((0xff & frameTypeHeader[0]) >> 3) % 0x0F;
            int frameQuality = ((0xff & frameTypeHeader[0]) >> 2) & 0x01;
            int blockSize = BLOCK_SIZES[frameType];
    
            if (blockSize + 1 > maxLen) {
                // We can't read the full frame, so consume the remaining
                // bytes to end processing the AMR stream.
                return maxLen;
            }
    
            if (blockSize == 0) {
                return 1;
            }
    
            byte[] v = new byte[blockSize];
            stream.read(v, 0, blockSize);
            mOffset += blockSize;
    
            int[] bits = new int[blockSize * 8];
            int ii = 0;
            int value = 0xff & v[ii];
            for (int i = 0; i < blockSize * 8; i++) {
                bits[i] = ((value & 0x80) >> 7);
                value <<= 1;
                if ((i & 0x07) == 0x07 && i < blockSize * 8 - 1) {
                    ii += 1;
                    value = 0xff & v[ii];
                }
            }
    
        int[] gain;
            switch (frameType) {
        case 0:
                mBitRate = 5;
                gain = new int[4];
                gain[0] =
                    0x01 * bits[28] +
                    0x02 * bits[29] +
                    0x04 * bits[30] +
                    0x08 * bits[31] +
                    0x10 * bits[46] +
                    0x20 * bits[47] +
            0x40 * bits[48] +
            0x80 * bits[49];
                gain[1] = gain[0];
                gain[2] =
                    0x01 * bits[32] +
                    0x02 * bits[33] +
                    0x04 * bits[34] +
                    0x08 * bits[35] +
                    0x10 * bits[40] +
                    0x20 * bits[41] +
                    0x40 * bits[42] +
                    0x80 * bits[43];
                gain[3] = gain[2];
    
                for (int i = 0; i < 4; i++) {
            int index = gain[i] * 4 + (i & 1) * 2 + 1;
            int gFac = GAIN_FAC_MR475[index];
    
                    double log2 = Math.log(gFac) / Math.log(2);
                    int exp = (int)log2;
                    int frac = (int)((log2 - exp) * 32768);
    
            exp -= 12;
            int tmp = exp * 49320;
            tmp += ((frac * 24660) >> 15) * 2;
            int quaEner = ((tmp * 8192) + 0x8000) >> 16;
    
                    int gcode0 =
                        (385963008 +
                         prevEner[0] * 5571 +
                         prevEner[1] * 4751 +
                         prevEner[2] * 2785 +
                         prevEner[3] * 1556) >> 15;
    
                    prevEner[3] = prevEner[2];
                    prevEner[2] = prevEner[1];
                    prevEner[1] = prevEner[0];
                    prevEner[0] = quaEner;
    
                    int frameGainEstimate = (gcode0 * gFac) >> 24;
    
                    addFrame(frameOffset, blockSize + 1, frameGainEstimate);
                }
    
            break;
    
            case 1:
                mBitRate = 5;
                gain = new int[4];
                gain[0] =
                    0x01 * bits[24] +
                    0x02 * bits[25] +
                    0x04 * bits[26] +
                    0x08 * bits[36] +
                    0x10 * bits[45] +
                    0x20 * bits[55];
                gain[1] =
                    0x01 * bits[27] +
                    0x02 * bits[28] +
                    0x04 * bits[29] +
                    0x08 * bits[37] +
                    0x10 * bits[46] +
                    0x20 * bits[56];
                gain[2] =
                    0x01 * bits[30] +
                    0x02 * bits[31] +
                    0x04 * bits[32] +
                    0x08 * bits[38] +
                    0x10 * bits[47] +
                    0x20 * bits[57];
                gain[3] =
                    0x01 * bits[33] +
                    0x02 * bits[34] +
                    0x04 * bits[35] +
                    0x08 * bits[39] +
                    0x10 * bits[48] +
                    0x20 * bits[58];
    
                for (int i = 0; i < 4; i++) {
                    int gcode0 =
                        (385963008 +
                         prevEner[0] * 5571 +
                         prevEner[1] * 4751 +
                         prevEner[2] * 2785 +
                         prevEner[3] * 1556) >> 15;
                    int quaEner = QUA_ENER_MR515[gain[i]];
                    int gFac = GAIN_FAC_MR515[gain[i]];
    
                    prevEner[3] = prevEner[2];
                    prevEner[2] = prevEner[1];
                    prevEner[1] = prevEner[0];
                    prevEner[0] = quaEner;
    
                    int frameGainEstimate = (gcode0 * gFac) >> 24;
    
                    addFrame(frameOffset, blockSize + 1, frameGainEstimate);
                }
    
                break;
            case 7:
                mBitRate = 12;
                int[] adaptiveIndex = new int[4];
                int[] adaptiveGain = new int[4];
                int[] fixedGain = new int[4];
                int[][] pulse = new int[4][];
                for (int i = 0; i < 4; i++) {
                    pulse[i] = new int[10];
                }
                getMR122Params(bits, adaptiveIndex, adaptiveGain, fixedGain, pulse);
    
                int T0 = 0;
                for (int subframe = 0; subframe < 4; subframe++) {
                    int[] code = new int[40];
                    for (int i = 0; i < 40; i++) {
                        code[i] = 0;
                    }
    
                    int sign;
                    for (int j = 0; j < 5; j++) {
                        if (((pulse[subframe][j] >> 3) & 1) == 0) {
                            sign = 4096;
                        } else {
                            sign = -4096;
                        }
    
                        int pos1 = j + GRAY[pulse[subframe][j] & 7] * 5;
                        int pos2 = j + GRAY[pulse[subframe][j + 5] & 7] * 5;
                        code[pos1] = sign;
                        if (pos2 < pos1) {
                            sign = -sign;
                        }
                        code[pos2] = code[pos2] + sign;
                    }
    
                    int index = adaptiveIndex[subframe];
    
                    if (subframe == 0 || subframe == 2) {
                        if (index < 463) {
                            T0 = (index + 5) / 6 + 17;
                        } else {
                            T0 = index - 368;
                        }
                    } else {
                        int pitMin = 18;
                        int pitMax = 143;
                        int T0Min = T0 - 5;
                        if (T0Min < pitMin) {
                            T0Min = pitMin;
                        }
                        int T0Max = T0Min + 9;
                        if (T0Max > pitMax) {
                            T0Max = pitMax;
                            T0Min = T0Max - 9;
                        }
                        T0 = T0Min + (index + 5) / 6 - 1;
                    }
    
                    int pitSharp =
                        (QUA_GAIN_PITCH[adaptiveGain[subframe]] >> 2) << 2;
                    if (pitSharp > 16383) {
                        pitSharp = 32767;
                    } else {
                        pitSharp *= 2;
                    }
    
                    for (int j = T0; j < 40; j++) {
                        code[j] += (code[j - T0] * pitSharp) >> 15;
                    }
                
                    int enerCode = 0;
                    for (int j = 0; j < 40; j++) {
                        enerCode += code[j] * code[j];
                    }
    
                    if ((0x3fffffff <= enerCode) || (enerCode < 0)) {
                        enerCode = 0x7fffffff;
                    } else {
                        enerCode *= 2;
                    }
                    enerCode = ((enerCode + 0x8000) >> 16) * 52428;
    
                    double log2 = Math.log(enerCode) / Math.log(2);
                    int exp = (int)log2;
                    int frac = (int)((log2 - exp) * 32768);
                    enerCode = ((exp - 30) << 16) + (frac * 2);
    
                    int ener =
                        prevEner[0] * 44 +
                        prevEner[1] * 37 +
                        prevEner[2] * 22 +
                        prevEner[3] * 12;
    
                    ener = 2 * ener + 783741;
                    ener = (ener - enerCode) / 2;
    
                    int expGCode = ener >> 16;
                    int fracGCode = (ener >> 1) - (expGCode << 15);
    
                    int gCode0 = (int)
                        (Math.pow(2.0, expGCode + (fracGCode / 32768.0)) + 0.5);
    
                    if (gCode0 <= 2047) {
                        gCode0 = gCode0 << 4;
                    } else {
                        gCode0 = 32767;
                    }
    
                    index = fixedGain[subframe];
    
                    int gainCode =
                        ((gCode0 * QUA_GAIN_CODE[3 * index]) >> 15) << 1;
    
                    if ((gainCode & 0xFFFF8000) != 0) {
                        gainCode = 32767;
                    }
    
                    int frameGainEstimate = gainCode;
    
                    addFrame(frameOffset, blockSize + 1, frameGainEstimate);
    
                    int quaEnerMR122 = QUA_GAIN_CODE[3 * index + 1];
                    prevEner[3] = prevEner[2];
                    prevEner[2] = prevEner[1];
                    prevEner[1] = prevEner[0];
                    prevEner[0] = quaEnerMR122;
                }
                break;
    
            default:
                System.out.println("Unsupported frame type: " + frameType);
                addFrame(frameOffset, blockSize + 1, 1);
                break;
            }
    
            // Return number of bytes consumed
            return blockSize + 1;
        }
    
        void addFrame(int offset, int frameSize, int gain) {
            mFrameOffsets[mNumFrames] = offset;
            mFrameLens[mNumFrames] = frameSize;
            mFrameGains[mNumFrames] = gain;
            if (gain < mMinGain)
                mMinGain = gain;
            if (gain > mMaxGain)
                mMaxGain = gain;
    
            mNumFrames++;
            if (mNumFrames == mMaxFrames) {
                int newMaxFrames = mMaxFrames * 2;
    
                int[] newOffsets = new int[newMaxFrames];
                int[] newLens = new int[newMaxFrames];
                int[] newGains = new int[newMaxFrames];
                for (int i = 0; i < mNumFrames; i++) {
                    newOffsets[i] = mFrameOffsets[i];
                    newLens[i] = mFrameLens[i];
                    newGains[i] = mFrameGains[i];
                }
                mFrameOffsets = newOffsets;
                mFrameLens = newLens;
                mFrameGains = newGains;
                mMaxFrames = newMaxFrames;
            }
        }
    
        public void WriteFile(File outputFile, int startFrame, int numFrames)
                throws java.io.IOException {
            outputFile.createNewFile();
            FileInputStream in = new FileInputStream(mInputFile);
            FileOutputStream out = new FileOutputStream(outputFile);
    
            byte[] header = new byte[6];
            header[0] = '#';
            header[1] = '!';
            header[2] = 'A';
            header[3] = 'M';
            header[4] = 'R';
            header[5] = '\n';
            out.write(header, 0, 6);
    
            int maxFrameLen = 0;
            for (int i = 0; i < numFrames; i++) {
                if (mFrameLens[startFrame + i] > maxFrameLen)
                    maxFrameLen = mFrameLens[startFrame + i];
            }
            byte[] buffer = new byte[maxFrameLen];
            int pos = 0;
            for (int i = 0; i < numFrames; i++) {
                int skip = mFrameOffsets[startFrame + i] - pos;
                int len = mFrameLens[startFrame + i];
                if (skip < 0) {
                    continue;
                }
                if (skip > 0) {
                    in.skip(skip);
                    pos += skip;
                }
                in.read(buffer, 0, len);
                out.write(buffer, 0, len);
                pos += len;
            }
    
            in.close();
            out.close();
        }
    
        void getMR122Params(int[] bits,
                            int[] adaptiveIndex,
                            int[] adaptiveGain,
                            int[] fixedGain,
                            int[][] pulse) {
            adaptiveIndex[0] =
                0x01 * bits[45] +
                0x02 * bits[43] +
                0x04 * bits[41] +
                0x08 * bits[39] +
                0x10 * bits[37] +
                0x20 * bits[35] +
                0x40 * bits[33] +
                0x80 * bits[31] +
                0x100 * bits[29];
            adaptiveIndex[1] =
                0x01 * bits[242] +
                0x02 * bits[79] +
                0x04 * bits[77] +
                0x08 * bits[75] +
                0x10 * bits[73] +
                0x20 * bits[71];
            adaptiveIndex[2] =
                0x01 * bits[46] +
                0x02 * bits[44] +
                0x04 * bits[42] +
                0x08 * bits[40] +
                0x10 * bits[38] +
                0x20 * bits[36] +
                0x40 * bits[34] +
                0x80 * bits[32] +
                0x100 * bits[30];
            adaptiveIndex[3] =
                0x01 * bits[243] +
                0x02 * bits[80] +
                0x04 * bits[78] +
                0x08 * bits[76] +
                0x10 * bits[74] +
                0x20 * bits[72];
    
            adaptiveGain[0] =
                0x01 * bits[88] +
                0x02 * bits[55] +
                0x04 * bits[51] +
                0x08 * bits[47];
            adaptiveGain[1] =
                0x01 * bits[89] +
                0x02 * bits[56] +
                0x04 * bits[52] +
                0x08 * bits[48];
            adaptiveGain[2] =
                0x01 * bits[90] +
                0x02 * bits[57] +
                0x04 * bits[53] +
                0x08 * bits[49];
            adaptiveGain[3] =
                0x01 * bits[91] +
                0x02 * bits[58] +
                0x04 * bits[54] +
                0x08 * bits[50];
    
            fixedGain[0] =
                0x01 * bits[104] +
                0x02 * bits[92] +
                0x04 * bits[67] +
                0x08 * bits[63] +
                0x10 * bits[59];
            fixedGain[1] =
                0x01 * bits[105] +
                0x02 * bits[93] +
                0x04 * bits[68] +
                0x08 * bits[64] +
                0x10 * bits[60];
            fixedGain[2] =
                0x01 * bits[106] +
                0x02 * bits[94] +
                0x04 * bits[69] +
                0x08 * bits[65] +
                0x10 * bits[61];
            fixedGain[3] =
                0x01 * bits[107] +
                0x02 * bits[95] +
                0x04 * bits[70] +
                0x08 * bits[66] +
                0x10 * bits[62];
    
            pulse[0][0] =
                0x01 * bits[122] +
                0x02 * bits[123] +
                0x04 * bits[124] +
                0x08 * bits[96];
            pulse[0][1] =
                0x01 * bits[125] +
                0x02 * bits[126] +
                0x04 * bits[127] +
                0x08 * bits[100];
            pulse[0][2] =
                0x01 * bits[128] +
                0x02 * bits[129] +
                0x04 * bits[130] +
                0x08 * bits[108];
            pulse[0][3] =
                0x01 * bits[131] +
                0x02 * bits[132] +
                0x04 * bits[133] +
                0x08 * bits[112];
            pulse[0][4] =
                0x01 * bits[134] +
                0x02 * bits[135] +
                0x04 * bits[136] +
                0x08 * bits[116];
            pulse[0][5] =
                0x01 * bits[182] +
                0x02 * bits[183] +
                0x04 * bits[184];
            pulse[0][6] =
                0x01 * bits[185] +
                0x02 * bits[186] +
                0x04 * bits[187];
            pulse[0][7] =
                0x01 * bits[188] +
                0x02 * bits[189] +
                0x04 * bits[190];
            pulse[0][8] =
                0x01 * bits[191] +
                0x02 * bits[192] +
                0x04 * bits[193];
            pulse[0][9] =
                0x01 * bits[194] +
                0x02 * bits[195] +
                0x04 * bits[196];
            pulse[1][0] =
                0x01 * bits[137] +
                0x02 * bits[138] +
                0x04 * bits[139] +
                0x08 * bits[97];
            pulse[1][1] =
                0x01 * bits[140] +
                0x02 * bits[141] +
                0x04 * bits[142] +
                0x08 * bits[101];
            pulse[1][2] =
                0x01 * bits[143] +
                0x02 * bits[144] +
                0x04 * bits[145] +
                0x08 * bits[109];
            pulse[1][3] =
                0x01 * bits[146] +
                0x02 * bits[147] +
                0x04 * bits[148] +
                0x08 * bits[113];
            pulse[1][4] =
                0x01 * bits[149] +
                0x02 * bits[150] +
                0x04 * bits[151] +
                0x08 * bits[117];
            pulse[1][5] =
                0x01 * bits[197] +
                0x02 * bits[198] +
                0x04 * bits[199];
            pulse[1][6] =
                0x01 * bits[200] +
                0x02 * bits[201] +
                0x04 * bits[202];
            pulse[1][7] =
                0x01 * bits[203] +
                0x02 * bits[204] +
                0x04 * bits[205];
            pulse[1][8] =
                0x01 * bits[206] +
                0x02 * bits[207] +
                0x04 * bits[208];
            pulse[1][9] =
                0x01 * bits[209] +
                0x02 * bits[210] +
                0x04 * bits[211];
            pulse[2][0] =
                0x01 * bits[152] +
                0x02 * bits[153] +
                0x04 * bits[154] +
                0x08 * bits[98];
            pulse[2][1] =
                0x01 * bits[155] +
                0x02 * bits[156] +
                0x04 * bits[157] +
                0x08 * bits[102];
            pulse[2][2] =
                0x01 * bits[158] +
                0x02 * bits[159] +
                0x04 * bits[160] +
                0x08 * bits[110];
            pulse[2][3] =
                0x01 * bits[161] +
                0x02 * bits[162] +
                0x04 * bits[163] +
                0x08 * bits[114];
            pulse[2][4] =
                0x01 * bits[164] +
                0x02 * bits[165] +
                0x04 * bits[166] +
                0x08 * bits[118];
            pulse[2][5] =
                0x01 * bits[212] +
                0x02 * bits[213] +
                0x04 * bits[214];
            pulse[2][6] =
                0x01 * bits[215] +
                0x02 * bits[216] +
                0x04 * bits[217];
            pulse[2][7] =
                0x01 * bits[218] +
                0x02 * bits[219] +
                0x04 * bits[220];
            pulse[2][8] =
                0x01 * bits[221] +
                0x02 * bits[222] +
                0x04 * bits[223];
            pulse[2][9] =
                0x01 * bits[224] +
                0x02 * bits[225] +
                0x04 * bits[226];
            pulse[3][0] =
                0x01 * bits[167] +
                0x02 * bits[168] +
                0x04 * bits[169] +
                0x08 * bits[99];
            pulse[3][1] =
                0x01 * bits[170] +
                0x02 * bits[171] +
                0x04 * bits[172] +
                0x08 * bits[103];
            pulse[3][2] =
                0x01 * bits[173] +
                0x02 * bits[174] +
                0x04 * bits[175] +
                0x08 * bits[111];
            pulse[3][3] =
                0x01 * bits[176] +
                0x02 * bits[177] +
                0x04 * bits[178] +
                0x08 * bits[115];
            pulse[3][4] =
                0x01 * bits[179] +
                0x02 * bits[180] +
                0x04 * bits[181] +
                0x08 * bits[119];
            pulse[3][5] =
                0x01 * bits[227] +
                0x02 * bits[228] +
                0x04 * bits[229];
            pulse[3][6] =
                0x01 * bits[230] +
                0x02 * bits[231] +
                0x04 * bits[232];
            pulse[3][7] =
                0x01 * bits[233] +
                0x02 * bits[234] +
                0x04 * bits[235];
            pulse[3][8] =
                0x01 * bits[236] +
                0x02 * bits[237] +
                0x04 * bits[238];
            pulse[3][9] =
                0x01 * bits[239] +
                0x02 * bits[240] +
                0x04 * bits[241];
        }
    
        // Block size in bytes for each of the 16 frame types, not
        // counting the initial byte that indicates the frame type.
        // Can be used to skip over unsupported frame types.
        static private int BLOCK_SIZES[] = {
            12, 13, 15, 17, 19, 20, 26, 31,
            5, 0, 0, 0, 0, 0, 0, 0 };
    
        static private int GAIN_FAC_MR515[] = {
            28753, 2785, 6594, 7413, 10444, 1269, 4423, 1556,
            12820, 2498, 4833, 2498, 7864, 1884, 3153, 1802,
            20193, 3031, 5857, 4014, 8970, 1392, 4096, 655,
            13926, 3112, 4669, 2703, 6553, 901, 2662, 655,
            23511, 2457, 5079, 4096, 8560, 737, 4259, 2088,
            12288, 1474, 4628, 1433, 7004, 737, 2252, 1228,
            17326, 2334, 5816, 3686, 8601, 778, 3809, 614,
            9256, 1761, 3522, 1966, 5529, 737, 3194, 778
        };
    
        static private int QUA_ENER_MR515[] = {
            17333, -3431, 4235, 5276, 8325, -10422, 683, -8609,
            10148, -4398, 1472, -4398, 5802, -6907, -2327, -7303,
            14189, -2678, 3181, -180, 6972, -9599, 0, -16305,
            10884, -2444, 1165, -3697, 4180, -13468, -3833, -16305,
            15543, -4546, 1913, 0, 6556, -15255, 347, -5993,
            9771, -9090, 1086, -9341, 4772, -15255, -5321, -10714,
            12827, -5002, 3118, -938, 6598, -14774, -646, -16879,
            7251, -7508, -1343, -6529, 2668, -15255, -2212, -2454, -14774
        };
    
        static private int QUA_GAIN_CODE[] = {
            159, -3776, -22731, 206, -3394, -20428,
            268, -3005, -18088, 349, -2615, -15739,
            419, -2345, -14113, 482, -2138, -12867,
            554, -1932, -11629, 637, -1726, -10387,
            733, -1518, -9139, 842, -1314, -7906,
            969, -1106, -6656, 1114, -900, -5416,
            1281, -694, -4173, 1473, -487, -2931,
            1694, -281, -1688, 1948, -75, -445,
            2241, 133, 801, 2577, 339, 2044,
            2963, 545, 3285, 3408, 752, 4530,
            3919, 958, 5772, 4507, 1165, 7016,
            5183, 1371, 8259, 5960, 1577, 9501,
            6855, 1784, 10745, 7883, 1991, 11988,
            9065, 2197, 13231, 10425, 2404, 14474,
            12510, 2673, 16096, 16263, 3060, 18429,
            21142, 3448, 20763, 27485, 3836, 23097};
    
        static private int GAIN_FAC_MR475[] = {
        812, 128, 542, 140, 2873, 1135, 2266, 3402,
        2067, 563, 12677, 647, 4132, 1798, 5601, 5285,
        7689, 374, 3735, 441, 10912, 2638, 11807, 2494,
        20490, 797, 5218, 675, 6724, 8354, 5282, 1696,
        1488, 428, 5882, 452, 5332, 4072, 3583, 1268,
        2469, 901, 15894, 1005, 14982, 3271, 10331, 4858,
        3635, 2021, 2596, 835, 12360, 4892, 12206, 1704,
        13432, 1604, 9118, 2341, 3968, 1538, 5479, 9936,
        3795, 417, 1359, 414, 3640, 1569, 7995, 3541,
        11405, 645, 8552, 635, 4056, 1377, 16608, 6124,
        11420, 700, 2007, 607, 12415, 1578, 11119, 4654,
        13680, 1708, 11990, 1229, 7996, 7297, 13231, 5715,
        2428, 1159, 2073, 1941, 6218, 6121, 3546, 1804,
        8925, 1802, 8679, 1580, 13935, 3576, 13313, 6237,
        6142, 1130, 5994, 1734, 14141, 4662, 11271, 3321,
        12226, 1551, 13931, 3015, 5081, 10464, 9444, 6706,
        1689, 683, 1436, 1306, 7212, 3933, 4082, 2713,
        7793, 704, 15070, 802, 6299, 5212, 4337, 5357,
        6676, 541, 6062, 626, 13651, 3700, 11498, 2408,
        16156, 716, 12177, 751, 8065, 11489, 6314, 2256,
        4466, 496, 7293, 523, 10213, 3833, 8394, 3037,
        8403, 966, 14228, 1880, 8703, 5409, 16395, 4863,
        7420, 1979, 6089, 1230, 9371, 4398, 14558, 3363,
        13559, 2873, 13163, 1465, 5534, 1678, 13138, 14771,
        7338, 600, 1318, 548, 4252, 3539, 10044, 2364,
        10587, 622, 13088, 669, 14126, 3526, 5039, 9784,
        15338, 619, 3115, 590, 16442, 3013, 15542, 4168,
        15537, 1611, 15405, 1228, 16023, 9299, 7534, 4976,
        1990, 1213, 11447, 1157, 12512, 5519, 9475, 2644,
        7716, 2034, 13280, 2239, 16011, 5093, 8066, 6761,
        10083, 1413, 5002, 2347, 12523, 5975, 15126, 2899,
        18264, 2289, 15827, 2527, 16265, 10254, 14651, 11319,
        1797, 337, 3115, 397, 3510, 2928, 4592, 2670,
        7519, 628, 11415, 656, 5946, 2435, 6544, 7367,
        8238, 829, 4000, 863, 10032, 2492, 16057, 3551,
        18204, 1054, 6103, 1454, 5884, 7900, 18752, 3468,
        1864, 544, 9198, 683, 11623, 4160, 4594, 1644,
        3158, 1157, 15953, 2560, 12349, 3733, 17420, 5260,
        6106, 2004, 2917, 1742, 16467, 5257, 16787, 1680,
        17205, 1759, 4773, 3231, 7386, 6035, 14342, 10012,
        4035, 442, 4194, 458, 9214, 2242, 7427, 4217,
        12860, 801, 11186, 825, 12648, 2084, 12956, 6554,
        9505, 996, 6629, 985, 10537, 2502, 15289, 5006,
        12602, 2055, 15484, 1653, 16194, 6921, 14231, 5790,
        2626, 828, 5615, 1686, 13663, 5778, 3668, 1554,
        11313, 2633, 9770, 1459, 14003, 4733, 15897, 6291,
        6278, 1870, 7910, 2285, 16978, 4571, 16576, 3849,
        15248, 2311, 16023, 3244, 14459, 17808, 11847, 2763,
        1981, 1407, 1400, 876, 4335, 3547, 4391, 4210,
        5405, 680, 17461, 781, 6501, 5118, 8091, 7677,
        7355, 794, 8333, 1182, 15041, 3160, 14928, 3039,
        20421, 880, 14545, 852, 12337, 14708, 6904, 1920,
        4225, 933, 8218, 1087, 10659, 4084, 10082, 4533,
        2735, 840, 20657, 1081, 16711, 5966, 15873, 4578,
        10871, 2574, 3773, 1166, 14519, 4044, 20699, 2627,
        15219, 2734, 15274, 2186, 6257, 3226, 13125, 19480,
        7196, 930, 2462, 1618, 4515, 3092, 13852, 4277,
        10460, 833, 17339, 810, 16891, 2289, 15546, 8217,
        13603, 1684, 3197, 1834, 15948, 2820, 15812, 5327,
        17006, 2438, 16788, 1326, 15671, 8156, 11726, 8556,
        3762, 2053, 9563, 1317, 13561, 6790, 12227, 1936,
        8180, 3550, 13287, 1778, 16299, 6599, 16291, 7758,
        8521, 2551, 7225, 2645, 18269, 7489, 16885, 2248,
        17882, 2884, 17265, 3328, 9417, 20162, 11042, 8320,
        1286, 620, 1431, 583, 5993, 2289, 3978, 3626,
        5144, 752, 13409, 830, 5553, 2860, 11764, 5908,
        10737, 560, 5446, 564, 13321, 3008, 11946, 3683,
        19887, 798, 9825, 728, 13663, 8748, 7391, 3053,
        2515, 778, 6050, 833, 6469, 5074, 8305, 2463,
        6141, 1865, 15308, 1262, 14408, 4547, 13663, 4515,
        3137, 2983, 2479, 1259, 15088, 4647, 15382, 2607,
        14492, 2392, 12462, 2537, 7539, 2949, 12909, 12060,
        5468, 684, 3141, 722, 5081, 1274, 12732, 4200,
        15302, 681, 7819, 592, 6534, 2021, 16478, 8737,
        13364, 882, 5397, 899, 14656, 2178, 14741, 4227,
        14270, 1298, 13929, 2029, 15477, 7482, 15815, 4572,
        2521, 2013, 5062, 1804, 5159, 6582, 7130, 3597,
        10920, 1611, 11729, 1708, 16903, 3455, 16268, 6640,
        9306, 1007, 9369, 2106, 19182, 5037, 12441, 4269,
        15919, 1332, 15357, 3512, 11898, 14141, 16101, 6854,
        2010, 737, 3779, 861, 11454, 2880, 3564, 3540,
        9057, 1241, 12391, 896, 8546, 4629, 11561, 5776,
        8129, 589, 8218, 588, 18728, 3755, 12973, 3149,
        15729, 758, 16634, 754, 15222, 11138, 15871, 2208,
        4673, 610, 10218, 678, 15257, 4146, 5729, 3327,
        8377, 1670, 19862, 2321, 15450, 5511, 14054, 5481,
        5728, 2888, 7580, 1346, 14384, 5325, 16236, 3950,
        15118, 3744, 15306, 1435, 14597, 4070, 12301, 15696,
        7617, 1699, 2170, 884, 4459, 4567, 18094, 3306,
        12742, 815, 14926, 907, 15016, 4281, 15518, 8368,
        17994, 1087, 2358, 865, 16281, 3787, 15679, 4596,
        16356, 1534, 16584, 2210, 16833, 9697, 15929, 4513,
        3277, 1085, 9643, 2187, 11973, 6068, 9199, 4462,
        8955, 1629, 10289, 3062, 16481, 5155, 15466, 7066,
        13678, 2543, 5273, 2277, 16746, 6213, 16655, 3408,
        20304, 3363, 18688, 1985, 14172, 12867, 15154, 15703,
        4473, 1020, 1681, 886, 4311, 4301, 8952, 3657,
        5893, 1147, 11647, 1452, 15886, 2227, 4582, 6644,
        6929, 1205, 6220, 799, 12415, 3409, 15968, 3877,
        19859, 2109, 9689, 2141, 14742, 8830, 14480, 2599,
        1817, 1238, 7771, 813, 19079, 4410, 5554, 2064,
        3687, 2844, 17435, 2256, 16697, 4486, 16199, 5388,
        8028, 2763, 3405, 2119, 17426, 5477, 13698, 2786,
        19879, 2720, 9098, 3880, 18172, 4833, 17336, 12207,
        5116, 996, 4935, 988, 9888, 3081, 6014, 5371,
        15881, 1667, 8405, 1183, 15087, 2366, 19777, 7002,
        11963, 1562, 7279, 1128, 16859, 1532, 15762, 5381,
        14708, 2065, 20105, 2155, 17158, 8245, 17911, 6318,
        5467, 1504, 4100, 2574, 17421, 6810, 5673, 2888,
        16636, 3382, 8975, 1831, 20159, 4737, 19550, 7294,
        6658, 2781, 11472, 3321, 19397, 5054, 18878, 4722,
        16439, 2373, 20430, 4386, 11353, 26526, 11593, 3068,
        2866, 1566, 5108, 1070, 9614, 4915, 4939, 3536,
        7541, 878, 20717, 851, 6938, 4395, 16799, 7733,
        10137, 1019, 9845, 964, 15494, 3955, 15459, 3430,
        18863, 982, 20120, 963, 16876, 12887, 14334, 4200,
        6599, 1220, 9222, 814, 16942, 5134, 5661, 4898,
        5488, 1798, 20258, 3962, 17005, 6178, 17929, 5929,
        9365, 3420, 7474, 1971, 19537, 5177, 19003, 3006,
        16454, 3788, 16070, 2367, 8664, 2743, 9445, 26358,
        10856, 1287, 3555, 1009, 5606, 3622, 19453, 5512,
        12453, 797, 20634, 911, 15427, 3066, 17037, 10275,
        18883, 2633, 3913, 1268, 19519, 3371, 18052, 5230,
        19291, 1678, 19508, 3172, 18072, 10754, 16625, 6845,
        3134, 2298, 10869, 2437, 15580, 6913, 12597, 3381,
        11116, 3297, 16762, 2424, 18853, 6715, 17171, 9887,
        12743, 2605, 8937, 3140, 19033, 7764, 18347, 3880,
        20475, 3682, 19602, 3380, 13044, 19373, 10526, 23124};
    
        static private int GRAY[] = {0, 1, 3, 2, 5, 6, 4, 7};
    
        static private int QUA_GAIN_PITCH[] = {
            0, 3277, 6556, 8192, 9830, 11469, 12288, 13107, 13926,
            14746, 15565, 16384, 17203, 18022, 18842, 19661};
    
        /** For debugging
        public static void main(String[] argv) throws Exception {
            File f = new File("");
            CheapAMR c = new CheapAMR();
            c.ReadFile(f);
            c.WriteFile(new File(""),
                        0, c.getNumFrames());
        } **/
    };

    CheapMP3

    /*
     * Copyright (C) 2008 Google Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.hwtt.soundfile;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    
    /**
     * CheapMP3 represents an MP3 file by doing a "cheap" scan of the file, parsing
     * the frame headers only and getting an extremely rough estimate of the volume
     * level of each frame.
     * 
     * TODO: Useful unit tests might be to look for sync in various places: FF FA FF
     * FB 00 FF FA FF FF FA ([ 00 ] * 12) FF FA ([ 00 ] * 13) FF FA
     */
    public class CheapMP3 extends CheapSoundFile {
        public static Factory getFactory() {
            return new Factory() {
                public CheapSoundFile create() {
                    return new CheapMP3();
                }
    
                public String[] getSupportedExtensions() {
                    return new String[] { "mp3" };
                }
            };
        }
    
        // Member variables representing frame data
        private int mNumFrames;
        private int[] mFrameOffsets;
        private int[] mFrameLens;
        private int[] mFrameGains;
        private int mFileSize;
        private int mAvgBitRate;
        private int mGlobalSampleRate;
        private int mGlobalChannels;
    
        // Member variables used during initialization
        private int mMaxFrames;
        private int mBitrateSum;
        private int mMinGain;
        private int mMaxGain;
    
        public CheapMP3() {
        }
    
        public int getNumFrames() {
            return mNumFrames;
        }
    
        public int[] getFrameOffsets() {
            return mFrameOffsets;
        }
    
        public int getSamplesPerFrame() {
            return 1152;
        }
    
        public int[] getFrameLens() {
            return mFrameLens;
        }
    
        public int[] getFrameGains() {
            return mFrameGains;
        }
    
        public int getFileSizeBytes() {
            return mFileSize;
        }
    
        public int getAvgBitrateKbps() {
            return mAvgBitRate;
        }
    
        public int getSampleRate() {
            return mGlobalSampleRate;
        }
    
        public int getChannels() {
            return mGlobalChannels;
        }
    
        public String getFiletype() {
            return "MP3";
        }
    
        /**
         * MP3 supports seeking into the middle of the file, no header needed, so
         * this method is supported to hear exactly what a "cut" of the file sounds
         * like without needing to actually save a file to disk first.
         */
        public int getSeekableFrameOffset(int frame) {
            if (frame <= 0) {
                return 0;
            } else if (frame >= mNumFrames) {
                return mFileSize;
            } else {
                return mFrameOffsets[frame];
            }
        }
    
        public void ReadFile(File inputFile) throws java.io.FileNotFoundException,
                java.io.IOException {
            super.ReadFile(inputFile);
            mNumFrames = 0;
            mMaxFrames = 64; // This will grow as needed
            mFrameOffsets = new int[mMaxFrames];
            mFrameLens = new int[mMaxFrames];
            mFrameGains = new int[mMaxFrames];
            mBitrateSum = 0;
            mMinGain = 255;
            mMaxGain = 0;
    
            // No need to handle filesizes larger than can fit in a 32-bit int
            mFileSize = (int) mInputFile.length();
    
            FileInputStream stream = new FileInputStream(mInputFile);
    
            int pos = 0;
            int offset = 0;
            byte[] buffer = new byte[12];
            while (pos < mFileSize - 12) {
                // Read 12 bytes at a time and look for a sync code (0xFF)
                while (offset < 12) {
                    offset += stream.read(buffer, offset, 12 - offset);
                }
                int bufferOffset = 0;
                while (bufferOffset < 12 && buffer[bufferOffset] != -1)
                    bufferOffset++;
    
                if (mProgressListener != null) {
                    boolean keepGoing = mProgressListener.reportProgress(pos * 1.0
                            / mFileSize);
                    if (!keepGoing) {
                        break;
                    }
                }
    
                if (bufferOffset > 0) {
                    // We didn't find a sync code (0xFF) at position 0;
                    // shift the buffer over and try again
                    for (int i = 0; i < 12 - bufferOffset; i++)
                        buffer[i] = buffer[bufferOffset + i];
                    pos += bufferOffset;
                    offset = 12 - bufferOffset;
                    continue;
                }
    
                // Check for MPEG 1 Layer III or MPEG 2 Layer III codes
                int mpgVersion = 0;
                if (buffer[1] == -6 || buffer[1] == -5) {
                    mpgVersion = 1;
                } else if (buffer[1] == -14 || buffer[1] == -13) {
                    mpgVersion = 2;
                } else {
                    bufferOffset = 1;
                    for (int i = 0; i < 12 - bufferOffset; i++)
                        buffer[i] = buffer[bufferOffset + i];
                    pos += bufferOffset;
                    offset = 12 - bufferOffset;
                    continue;
                }
    
                // The third byte has the bitrate and samplerate
                int bitRate;
                int sampleRate;
                if (mpgVersion == 1) {
                    // MPEG 1 Layer III
                    bitRate = BITRATES_MPEG1_L3[(buffer[2] & 0xF0) >> 4];
                    sampleRate = SAMPLERATES_MPEG1_L3[(buffer[2] & 0x0C) >> 2];
                } else {
                    // MPEG 2 Layer III
                    bitRate = BITRATES_MPEG2_L3[(buffer[2] & 0xF0) >> 4];
                    sampleRate = SAMPLERATES_MPEG2_L3[(buffer[2] & 0x0C) >> 2];
                }
    
                if (bitRate == 0 || sampleRate == 0) {
                    bufferOffset = 2;
                    for (int i = 0; i < 12 - bufferOffset; i++)
                        buffer[i] = buffer[bufferOffset + i];
                    pos += bufferOffset;
                    offset = 12 - bufferOffset;
                    continue;
                }
    
                // From here on we assume the frame is good
                mGlobalSampleRate = sampleRate;
                int padding = (buffer[2] & 2) >> 1;
                int frameLen = 144 * bitRate * 1000 / sampleRate + padding;
    
                int gain;
                if ((buffer[3] & 0xC0) == 0xC0) {
                    // 1 channel
                    mGlobalChannels = 1;
                    if (mpgVersion == 1) {
                        gain = ((buffer[10] & 0x01) << 7)
                                + ((buffer[11] & 0xFE) >> 1);
                    } else {
                        gain = ((buffer[9] & 0x03) << 6)
                                + ((buffer[10] & 0xFC) >> 2);
                    }
                } else {
                    // 2 channels
                    mGlobalChannels = 2;
                    if (mpgVersion == 1) {
                        gain = ((buffer[9] & 0x7F) << 1)
                                + ((buffer[10] & 0x80) >> 7);
                    } else {
                        gain = 0; // ???
                    }
                }
    
                mBitrateSum += bitRate;
    
                mFrameOffsets[mNumFrames] = pos;
                mFrameLens[mNumFrames] = frameLen;
                mFrameGains[mNumFrames] = gain;
                if (gain < mMinGain)
                    mMinGain = gain;
                if (gain > mMaxGain)
                    mMaxGain = gain;
    
                mNumFrames++;
                if (mNumFrames == mMaxFrames) {
                    // We need to grow our arrays. Rather than naively
                    // doubling the array each time, we estimate the exact
                    // number of frames we need and add 10% padding. In
                    // practice this seems to work quite well, only one
                    // resize is ever needed, however to avoid pathological
                    // cases we make sure to always double the size at a minimum.
    
                    mAvgBitRate = mBitrateSum / mNumFrames;
                    int totalFramesGuess = ((mFileSize / mAvgBitRate) * sampleRate) / 144000;
                    int newMaxFrames = totalFramesGuess * 11 / 10;
                    if (newMaxFrames < mMaxFrames * 2)
                        newMaxFrames = mMaxFrames * 2;
    
                    int[] newOffsets = new int[newMaxFrames];
                    int[] newLens = new int[newMaxFrames];
                    int[] newGains = new int[newMaxFrames];
                    for (int i = 0; i < mNumFrames; i++) {
                        newOffsets[i] = mFrameOffsets[i];
                        newLens[i] = mFrameLens[i];
                        newGains[i] = mFrameGains[i];
                    }
                    mFrameOffsets = newOffsets;
                    mFrameLens = newLens;
                    mFrameGains = newGains;
                    mMaxFrames = newMaxFrames;
                }
    
                stream.skip(frameLen - 12);
                pos += frameLen;
                offset = 0;
            }
    
            // We're done reading the file, do some postprocessing
            if (mNumFrames > 0)
                mAvgBitRate = mBitrateSum / mNumFrames;
            else
                mAvgBitRate = 0;
        }
    
        public void WriteFile(File outputFile, int startFrame, int numFrames)
                throws java.io.IOException {
            outputFile.createNewFile();
            FileInputStream in = new FileInputStream(mInputFile);
            FileOutputStream out = new FileOutputStream(outputFile);
            int maxFrameLen = 0;
            for (int i = 0; i < numFrames; i++) {
                if (mFrameLens[startFrame + i] > maxFrameLen)
                    maxFrameLen = mFrameLens[startFrame + i];
            }
            byte[] buffer = new byte[maxFrameLen];
            int pos = 0;
            for (int i = 0; i < numFrames; i++) {
                int skip = mFrameOffsets[startFrame + i] - pos;
                int len = mFrameLens[startFrame + i];
                if (skip > 0) {
                    in.skip(skip);
                    pos += skip;
                }
                in.read(buffer, 0, len);
                out.write(buffer, 0, len);
                pos += len;
            }
            in.close();
            out.close();
        }
    
        public void WriteFile(FileOutputStream outputFile, int startFrame,
                int numFrames) throws java.io.IOException {
            FileInputStream in = new FileInputStream(mInputFile);
            FileOutputStream out = outputFile;
            int maxFrameLen = 0;
            for (int i = 0; i < numFrames; i++) {
                if (mFrameLens[startFrame + i] > maxFrameLen)
                    maxFrameLen = mFrameLens[startFrame + i];
            }
            byte[] buffer = new byte[maxFrameLen];
            int pos = 0;
            for (int i = 0; i < numFrames; i++) {
                int skip = mFrameOffsets[startFrame + i] - pos;
                int len = mFrameLens[startFrame + i];
                if (skip > 0) {
                    in.skip(skip);
                    pos += skip;
                }
                in.read(buffer, 0, len);
                out.write(buffer, 0, len);
                pos += len;
            }
            in.close();
            out.close();
        }
    
        static private int BITRATES_MPEG1_L3[] = { 0, 32, 40, 48, 56, 64, 80, 96,
                112, 128, 160, 192, 224, 256, 320, 0 };
        static private int BITRATES_MPEG2_L3[] = { 0, 8, 16, 24, 32, 40, 48, 56,
                64, 80, 96, 112, 128, 144, 160, 0 };
        static private int SAMPLERATES_MPEG1_L3[] = { 44100, 48000, 32000, 0 };
        static private int SAMPLERATES_MPEG2_L3[] = { 22050, 24000, 16000, 0 };
    };

    CheapSoundFile

    /*
     * Copyright (C) 2008 Google Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.hwtt.soundfile;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    import java.security.MessageDigest;
    import java.util.ArrayList;
    import java.util.HashMap;
    /**
     * CheapSoundFile is the parent class of several subclasses that each
     * do a "cheap" scan of various sound file formats, parsing as little
     * as possible in order to understand the high-level frame structure
     * and get a rough estimate of the volume level of each frame.  Each
     * subclass is able to:
     *  - open a sound file
     *  - return the sample rate and number of frames
     *  - return an approximation of the volume level of each frame
     *  - write a new sound file with a subset of the frames
     *
     * A frame should represent no less than 1 ms and no more than 100 ms of
     * audio.  This is compatible with the native frame sizes of most audio
     * file formats already, but if not, this class should expose virtual
     * frames in that size range.
     */
    public class CheapSoundFile {
        public interface ProgressListener {
            /**
             * Will be called by the CheapSoundFile subclass periodically
             * with values between 0.0 and 1.0.  Return true to continue
             * loading the file, and false to cancel.
             */
            boolean reportProgress(double fractionComplete);
        }
    
        public interface Factory {
            public CheapSoundFile create();
            public String[] getSupportedExtensions();
        }
    
        static Factory[] sSubclassFactories = new Factory[] {
            CheapAAC.getFactory(),
            CheapAMR.getFactory(),
            CheapMP3.getFactory(),
            CheapWAV.getFactory(),
        };
    
        static ArrayList<String> sSupportedExtensions = new ArrayList<String>();
        static HashMap<String, Factory> sExtensionMap =
            new HashMap<String, Factory>();
    
        static {
            for (Factory f : sSubclassFactories) {
                for (String extension : f.getSupportedExtensions()) {
                    sSupportedExtensions.add(extension);
                    sExtensionMap.put(extension, f);
                }
            }
        }
    
        /**
         * Static method to create the appropriate CheapSoundFile subclass
         * given a filename.
         *
         * TODO: make this more modular rather than hardcoding the logic
         */
        public static CheapSoundFile create(String fileName,
                                            ProgressListener progressListener)
            throws java.io.FileNotFoundException,
                   java.io.IOException {
            File f = new File(fileName);
            if (!f.exists()) {
                throw new java.io.FileNotFoundException(fileName);
            }
            String name = f.getName().toLowerCase();
            System.out.println("name-->"+name);
            String[] components = name.split("\\.");
            for(int i=0;i<components.length;i++){
                System.out.println("components["+i+"]-->"+components[i]);
            }
            if (components.length < 2) {
                return null;
            }
            Factory factory = sExtensionMap.get(components[components.length - 1]);
            if (factory == null) {
                return null;
            }
            CheapSoundFile soundFile = factory.create();
            soundFile.setProgressListener(progressListener);
            soundFile.ReadFile(f);
            return soundFile;
        }
    
        public static boolean isFilenameSupported(String filename) {
            String[] components = filename.toLowerCase().split("\\.");
            if (components.length < 2) {
                return false;
            }
            return sExtensionMap.containsKey(components[components.length - 1]);
        }
    
        /**
         * Return the filename extensions that are recognized by one of
         * our subclasses.
         */
        public static String[] getSupportedExtensions() {
            return sSupportedExtensions.toArray(
                new String[sSupportedExtensions.size()]);
        }
    
        protected ProgressListener mProgressListener = null;
        protected File mInputFile = null;
    
        protected CheapSoundFile() {
        }
    
        public void ReadFile(File inputFile)
            throws java.io.FileNotFoundException,
                   java.io.IOException {
            mInputFile = inputFile;
        }
        public void ReadStrem(InputStream inputStream) throws java.io.IOException {
            
        }
    
        public void setProgressListener(ProgressListener progressListener) {
            mProgressListener = progressListener;
        }
    
        public int getNumFrames() {
            return 0;
        }
    
        public int getSamplesPerFrame() {
            return 0;
        }
    
        public int[] getFrameOffsets() {
            return null;
        }
    
        public int[] getFrameLens() {
            return null;
        }
    
        public int[] getFrameGains() {
            return null;
        }
    
        public int getFileSizeBytes() {
            return 0;
        }
    
        public int getAvgBitrateKbps() {
            return 0;
        }
    
        public int getSampleRate() {
            return 0;
        }
    
        public int getChannels() {
            return 0;
        }
    
        public String getFiletype() {
            return "Unknown";
        }
    
        /**
         * If and only if this particular file format supports seeking
         * directly into the middle of the file without reading the rest of
         * the header, this returns the byte offset of the given frame,
         * otherwise returns -1.
         */
        public int getSeekableFrameOffset(int frame) {
            return -1;
        }
    
        private static final char[] HEX_CHARS = {
            '0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
        public static String bytesToHex (byte hash[]) {
            char buf[] = new char[hash.length * 2];
            for (int i = 0, x = 0; i < hash.length; i++) {
                buf[x++] = HEX_CHARS[(hash[i] >>> 4) & 0xf];
                buf[x++] = HEX_CHARS[hash[i] & 0xf];
            }
            return new String(buf);
        }
    
        public String computeMd5OfFirst10Frames()
                throws java.io.FileNotFoundException,
                       java.io.IOException,
                       java.security.NoSuchAlgorithmException {
            int[] frameOffsets = getFrameOffsets();
            int[] frameLens = getFrameLens();
            int numFrames = frameLens.length;
            if (numFrames > 10) {
                numFrames = 10;
            }
    
            MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
            FileInputStream in = new FileInputStream(mInputFile);
            int pos = 0;
            for (int i = 0; i < numFrames; i++) {
                int skip = frameOffsets[i] - pos;
                int len = frameLens[i];
                if (skip > 0) {
                    in.skip(skip);
                    pos += skip;
                }
                byte[] buffer = new byte[len];
                in.read(buffer, 0, len);
                digest.update(buffer);
                pos += len;
            }
            in.close();
            byte[] hash = digest.digest();
            return bytesToHex(hash);
        }
    
        public void WriteFile(File outputFile, int startFrame, int numFrames)
                throws java.io.IOException {
        }
    };

    CheapWAV

    /*
     * Copyright (C) 2008 Google Inc.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.hwtt.soundfile;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.InputStream;
    
    /**
     * CheapWAV represents a standard 16-bit WAV file, splitting it into
     * artificial frames of 20 ms and taking the maximum of each frame to
     * get an approximation of the waveform contour.
     */
    public class CheapWAV extends CheapSoundFile {
        public static Factory getFactory() {
            return new Factory() {
                public CheapSoundFile create() {
                    return new CheapWAV();
                }
                public String[] getSupportedExtensions() {
                    return new String[] { "wav" };
                }
            };
        }
    
        // Member variables containing frame info
        private int mNumFrames;
        private int[] mFrameOffsets;
        private int[] mFrameLens;
        private int[] mFrameGains;
        private int mFrameBytes;
        private int mFileSize;
        private int mSampleRate;
        private int mChannels;
        // Member variables used during initialization
        private int mOffset;
    
        public CheapWAV() {
        }
    
        public int getNumFrames() {
            return mNumFrames;
        }
    
        public int getSamplesPerFrame() {
            return mSampleRate / 50;
        }
    
        public int[] getFrameOffsets() {
            return mFrameOffsets;
        }
    
        public int[] getFrameLens() {
            return mFrameLens;
        }
    
        public int[] getFrameGains() {
            return mFrameGains;
        }
    
        public int getFileSizeBytes() {
            return mFileSize;        
        }
    
        public int getAvgBitrateKbps() {
            return mSampleRate * mChannels * 2 / 1024;
        }
    
        public int getSampleRate() {
            return mSampleRate;
        }
    
        public int getChannels() {
            return mChannels;
        }
    
        public String getFiletype() {
            return "WAV";
        }
    
        public void ReadFile(File inputFile)
                throws java.io.FileNotFoundException,
                       java.io.IOException {
            super.ReadFile(inputFile);
            mFileSize = (int)mInputFile.length();
    
            if (mFileSize < 128) {
                throw new java.io.IOException("File too small to parse");
            }
    
            FileInputStream stream = new FileInputStream(mInputFile);
            byte[] header = new byte[12];
            stream.read(header, 0, 12);
            mOffset += 12;
            if (header[0] != 'R' ||
                header[1] != 'I' ||
                header[2] != 'F' ||
                header[3] != 'F' ||
                header[8] != 'W' ||
                header[9] != 'A' ||
                header[10] != 'V' ||
                header[11] != 'E') {
                throw new java.io.IOException("Not a WAV file");
            }
    
            mChannels = 0;
            mSampleRate = 0;
            while (mOffset + 8 <= mFileSize) {
                byte[] chunkHeader = new byte[8];
                stream.read(chunkHeader, 0, 8);
                mOffset += 8;
    
                int chunkLen =
                    ((0xff & chunkHeader[7]) << 24) |
                    ((0xff & chunkHeader[6]) << 16) |
                    ((0xff & chunkHeader[5]) << 8) |
                    ((0xff & chunkHeader[4]));
    
                if (chunkHeader[0] == 'f' &&
                    chunkHeader[1] == 'm' &&
                    chunkHeader[2] == 't' &&
                    chunkHeader[3] == ' ') {
                    if (chunkLen < 16 || chunkLen > 1024) {
                        throw new java.io.IOException(
                            "WAV file has bad fmt chunk");
                    }
    
                    byte[] fmt = new byte[chunkLen];
                    stream.read(fmt, 0, chunkLen);
                    mOffset += chunkLen;
    
                    int format =
                        ((0xff & fmt[1]) << 8) |
                        ((0xff & fmt[0]));
                    mChannels =
                        ((0xff & fmt[3]) << 8) |
                        ((0xff & fmt[2]));
                    mSampleRate =
                        ((0xff & fmt[7]) << 24) |
                        ((0xff & fmt[6]) << 16) |
                        ((0xff & fmt[5]) << 8) |
                        ((0xff & fmt[4]));
    
                    if (format != 1) {
                        throw new java.io.IOException(
                            "Unsupported WAV file encoding");
                    }
    
                } else if (chunkHeader[0] == 'd' &&
                           chunkHeader[1] == 'a' &&
                           chunkHeader[2] == 't' &&
                           chunkHeader[3] == 'a') {
                    if (mChannels == 0 || mSampleRate == 0) {
                        throw new java.io.IOException(
                            "Bad WAV file: data chunk before fmt chunk");
                    }
    
                    int frameSamples = (mSampleRate * mChannels) / 50;
                    mFrameBytes = frameSamples * 2;
    
                    mNumFrames = (chunkLen + (mFrameBytes - 1)) / mFrameBytes;
                    mFrameOffsets = new int[mNumFrames];
                    mFrameLens = new int[mNumFrames];
                    mFrameGains = new int[mNumFrames];
    
                    byte[] oneFrame = new byte[mFrameBytes];
    
                    int i = 0;
                    int frameIndex = 0;
                    while (i < chunkLen) {
                        int oneFrameBytes = mFrameBytes;
                        if (i + oneFrameBytes > chunkLen) {
                            i = chunkLen - oneFrameBytes;
                        }
    
                        stream.read(oneFrame, 0, oneFrameBytes);
    
                        int maxGain = 0;
                        for (int j = 1; j < oneFrameBytes; j += 4 * mChannels) {
                            int val = java.lang.Math.abs(oneFrame[j]);
                            if (val > maxGain) {
                                maxGain = val;
                            }
                        }
    
                        mFrameOffsets[frameIndex] = mOffset;
                        mFrameLens[frameIndex] = oneFrameBytes;
                        mFrameGains[frameIndex] = maxGain;
    
                        frameIndex++;
                        mOffset += oneFrameBytes;
                        i += oneFrameBytes;
    
                        if (mProgressListener != null) {
                            boolean keepGoing = mProgressListener.reportProgress(
                                i * 1.0 / chunkLen);
                            if (!keepGoing) {
                                break;
                            }
                        }
                    }
    
                } else {
                    stream.skip(chunkLen);
                    mOffset += chunkLen;
                }
            }
        }
    
        public void WriteFile(File outputFile, int startFrame, int numFrames)
                throws java.io.IOException {
            outputFile.createNewFile();
            FileInputStream in = new FileInputStream(mInputFile);
            FileOutputStream out = new FileOutputStream(outputFile);
    
            long totalAudioLen = 0;
            for (int i = 0; i < numFrames; i++) {
                totalAudioLen += mFrameLens[startFrame + i];
            }
    
            long totalDataLen = totalAudioLen + 36;
            long longSampleRate = mSampleRate;
            long byteRate = mSampleRate * 2 * mChannels;
    
            byte[] header = new byte[44];
            header[0] = 'R';  // RIFF/WAVE header
            header[1] = 'I';
            header[2] = 'F';
            header[3] = 'F';
            header[4] = (byte) (totalDataLen & 0xff);
            header[5] = (byte) ((totalDataLen >> 8) & 0xff);
            header[6] = (byte) ((totalDataLen >> 16) & 0xff);
            header[7] = (byte) ((totalDataLen >> 24) & 0xff);
            header[8] = 'W';
            header[9] = 'A';
            header[10] = 'V';
            header[11] = 'E';
            header[12] = 'f';  // 'fmt ' chunk
            header[13] = 'm';
            header[14] = 't';
            header[15] = ' ';
            header[16] = 16;  // 4 bytes: size of 'fmt ' chunk
            header[17] = 0;
            header[18] = 0;
            header[19] = 0;
            header[20] = 1;  // format = 1
            header[21] = 0;
            header[22] = (byte) mChannels;
            header[23] = 0;
            header[24] = (byte) (longSampleRate & 0xff);
            header[25] = (byte) ((longSampleRate >> 8) & 0xff);
            header[26] = (byte) ((longSampleRate >> 16) & 0xff);
            header[27] = (byte) ((longSampleRate >> 24) & 0xff);
            header[28] = (byte) (byteRate & 0xff);
            header[29] = (byte) ((byteRate >> 8) & 0xff);
            header[30] = (byte) ((byteRate >> 16) & 0xff);
            header[31] = (byte) ((byteRate >> 24) & 0xff);
            header[32] = (byte) (2 * mChannels);  // block align
            header[33] = 0;
            header[34] = 16;  // bits per sample
            header[35] = 0;
            header[36] = 'd';
            header[37] = 'a';
            header[38] = 't';
            header[39] = 'a';
            header[40] = (byte) (totalAudioLen & 0xff);
            header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
            header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
            header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
            out.write(header, 0, 44);
    
            byte[] buffer = new byte[mFrameBytes];
            int pos = 0;
            for (int i = 0; i < numFrames; i++) {
                int skip = mFrameOffsets[startFrame + i] - pos;
                int len = mFrameLens[startFrame + i];
                if (skip < 0) {
                    continue;
                }
                if (skip > 0) {
                    in.skip(skip);
                    pos += skip;
                }
                in.read(buffer, 0, len);
                out.write(buffer, 0, len);
                pos += len;
            }
    
            in.close();
            out.close();
        }
    };

     (在F:\java\ringdemo)

  • 相关阅读:
    鼠标滑动察看
    jquery放大镜,可随意设置css
    常用的js插件配合滚轮事件左右滚动
    css的各种bug集合,主要针对ie6,7会出现
    ajax跨域请求及jsonp方式
    js随机生成一组指定区间的数组
    性能测试相关
    web窗体加载的过程。
    解密微软中间语言:MSIL
    .net应用程序版本控制
  • 原文地址:https://www.cnblogs.com/ct732003684/p/2864713.html
Copyright © 2020-2023  润新知