Аудиозапись с помощью AudioRecord

Я решил записывать звук с помощью AudioRecord, а не MediaRecorder, чтобы добиться максимального качества. Проблема в том, что приложение почему-то не работает. Есть 2 кнопки: запись и воспроизведение, запись используется для запуска и остановки записи (с использованием нового потока), а воспроизведение предполагает воспроизведение файла с использованием MediaPlayer.

Код:

    public class MyActivity extends Activity {

    AudioRecord recorder = null;
    int SAMPLE_RATE = 44100;
    int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
    int SOURCE = MediaRecorder.AudioSource.MIC;
    int CONFIG = AudioFormat.CHANNEL_IN_MONO;
    int BUFFER_SIZE;
    boolean isRecording = false;
    boolean isPlaying = false;
    String currentFileDir;
    byte[] b;
    File file;
    OutputStream FOS;
    int count =0;
    MediaPlayer mediaPlayer;
    Thread recordThread;

    private Button recordButton;
    private Button playButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        currentFileDir = getFilesDir().getAbsolutePath() + "Record.pcm";

        recordButton = (Button)findViewById(R.id.RecordButton);
        recordButton.setOnClickListener(new MyOCL());
        playButton = (Button)findViewById(R.id.PlayButton);
        playButton.setOnClickListener(new MyOCL());

    }

    protected void record(){
        file = new File(currentFileDir);
        BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE, CONFIG, ENCODING);
        recorder = new AudioRecord(SOURCE, SAMPLE_RATE, CONFIG, ENCODING, BUFFER_SIZE);
        isRecording = true;
        b = new byte[BUFFER_SIZE];

        try{
            FOS = new FileOutputStream(file);
        }
        catch (Exception e){Log.e("Open FOS", "new failed");}

        while (isRecording){
            recorder.read(b, 0, BUFFER_SIZE);
            try{
                FOS.write(b, count * BUFFER_SIZE, BUFFER_SIZE);
                count++;
            }
            catch (Exception e){Log.e("write FOS", "write failed");}
        }

        try {
            FOS.close();
        }
        catch (Exception e){Log.e("close FOS", "close failed");}
    }

    private class MyOCL implements View.OnClickListener{
        @Override
        public void onClick(View view){
            switch(view.getId()){
                case R.id.PlayButton:
                    if(isPlaying == false){
                        playButton.setText("Stop Playing");
                        setPlaying();
                        mediaPlayer.start();
                    }
                    else {
                        playButton.setText("Start Playing");
                        mediaPlayer.stop();
                        mediaPlayer.release();
                        mediaPlayer.reset();
                    }
                    break;
                case R.id.RecordButton:
                    if(isRecording == false) {
                        recordThread = new Thread(new Runnable() {
                            @Override
                            public void run() {
                                record();
                            }
                        });
                        recordThread.start();
                        recordButton.setText("Stop Recording");
                    }
                    else{
                        recordButton.setText("Start recording");
                        isRecording = false;
                    }
                    break;
            }
        }
    }

    protected void setPlaying(){
        try{
            mediaPlayer = new MediaPlayer();
            mediaPlayer.reset();
            mediaPlayer.setDataSource(currentFileDir);
            mediaPlayer.prepare();
            mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            //mediaPlayer.start();
        }
        catch (Exception e){
            Log.e("Play initialize", "Can't call prepare function" + e.getMessage());
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.my, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

person Community    schedule 08.01.2015    source источник
comment
Вы должны предоставить больше информации о том, как именно приложение не будет работать.   -  person Hartmut Pfitzinger    schedule 09.01.2015
comment
файл почему-то не сохраняется. Я добавлю несколько журналов позже, но проблема заключается в цикле while(isRecording), который кажется бесконечным.   -  person    schedule 09.01.2015


Ответы (1)


Вот мой код, который работает для меня:

public class MainActivity extends Activity
{
    AudioRecord record = null;
    AudioTrack track = null;

    boolean isRecording;
    int sampleRate = 44100;

    Button startRecord, stopRecord, playRecord = null;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setVolumeControlStream(AudioManager.MODE_IN_COMMUNICATION);
        startRecord = (Button) findViewById(R.id.start_recording);
        stopRecord = (Button) findViewById(R.id.stop_recording);
        playRecord = (Button) findViewById(R.id.play_recording);
        startRecord.setOnClickListener(new StartRecordListener());
        stopRecord.setOnClickListener(new StopRecordListener());
        playRecord.setOnClickListener(new PlayRecordListener());

        stopRecord.setEnabled(false);
    }

    private void startRecord()
    {
        File recordFile = new File(Environment.getExternalStorageDirectory(), "Record.pcm");
        try
        {
            recordFile.createNewFile();

            OutputStream outputStream = new FileOutputStream(recordFile);
            BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
            DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);

            int minBufferSize = AudioRecord.getMinBufferSize(sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);

            short[] audioData = new short[minBufferSize];

            record = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT,
                                     minBufferSize);
            record.startRecording();

            while (isRecording)
            {
                int numberOfShort = record.read(audioData, 0, minBufferSize);
                for (int i = 0; i < numberOfShort; i++)
                {
                    dataOutputStream.writeShort(audioData[i]);
                }
            }
            record.stop();
            dataOutputStream.close();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    public void playRecord()
    {
        File recordFile = new File(Environment.getExternalStorageDirectory(), "Record.pcm");

        int shortSizeInBytes = Short.SIZE / Byte.SIZE;
        int bufferSizeInBytes = (int) (recordFile.length() / shortSizeInBytes);
        short[] audioData = new short[bufferSizeInBytes];
        try
        {
            InputStream inputStream = new FileInputStream(recordFile);
            BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
            DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);

            int i = 0;
            while (dataInputStream.available() > 0)
            {
                audioData[i] = dataInputStream.readShort();
                i++;
            }

            dataInputStream.close();

            track = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,
                                   bufferSizeInBytes, AudioTrack.MODE_STREAM);

            track.play();
            track.write(audioData, 0, bufferSizeInBytes);
        }
        catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }


    public class StartRecordListener implements View.OnClickListener
    {
        @Override
        public void onClick(View v)
        {
            Thread recordThread = new Thread(new Runnable()
            {
                @Override
                public void run()
                {
                    isRecording = true;
                    MainActivity.this.startRecord();
                }
            });
            recordThread.start();
            startRecord.setEnabled(false);
            stopRecord.setEnabled(true);
        }
    }

    public class StopRecordListener implements View.OnClickListener
    {
        @Override
        public void onClick(View v)
        {
            isRecording = false;
            startRecord.setEnabled(true);
            stopRecord.setEnabled(false);
        }
    }

    public class PlayRecordListener implements View.OnClickListener
    {
        @Override
        public void onClick(View v)
        {
            MainActivity.this.playRecord();
        }
    }
}

XML-макет содержит 3 кнопки со следующими идентификаторами: start_recording, stop_recording, play_recording.

И добавьте к следующим разрешениям:

<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Удачи, и я надеюсь, что вы не против, что я использую 3 кнопки в приведенном выше коде.

person xry    schedule 08.01.2015
comment
Большое спасибо! Я взгляну на код. Я вижу, что вы использовали шорты и разобрали их на байты... почему так? - person ; 09.01.2015
comment
Вы можете сохранить байты, данные с помощью record.read(), непосредственно в свой выходной поток. - person xry; 09.01.2015
comment
Итак, есть ли какое-либо объяснение, почему, когда я инициализирую DataOutputStream с помощью OutputStream (вместо буфера посередине), звук становится намного быстрее? (думаю, что в 2 раза быстрее, чем на самом деле) - person ; 13.01.2015
comment
Извините, я не знаю, что вы имеете в виду. Вы можете попробовать пример выше и сделать некоторые измерения для него. - person xry; 13.01.2015
comment
Я попробовал это, и это здорово, мне просто интересно, почему это произошло - person ; 13.01.2015
comment
Я также нашел этот образец в блоге и отредактировал его для своего варианта использования. Если этот ответ помог вам и решил вашу проблему, пожалуйста, примите его, чтобы другие пользователи увидели, есть ли полезный ответ! - person xry; 13.01.2015