5

かなり長い間、Android で 2 つの WAVE ファイルをマージしようとしてきましたが、実際には正しく動作していないようです。

すべてが正常に見え、ファイルが読み取られ、出力ファイルに書き込まれます。出力ファイルは、後の段階でも読み取り可能であり、予想されるファイル サイズを持っています。

アプリのマージが完了した直後に問題が発生します。次のメッセージがログに表示されます: updateListener でエラーが発生しました。記録は中止されました。これは、からのメッセージであり、catch 句に到達したextAudioRecorderときに表示されますOnRecordPositionUpdateListener(例外には、次の detailMessage: write failed: EBADF (Bad file number)があります)。これは何も壊れていないようですので、私はこれについてあまり心配していません。

MediaPlayer インスタンスでを作成してMediaPlayerを呼び出そうとすると、本当の問題が発生します。マージsetDataSource(String path)されたファイルでこれを行うたびに、次のエラー メッセージがログに表示されます。ファイルが最初に完全に正常に再生されることに注意してください (この最初のファイルはメソッドによって作成されたものではありません)。このエラー メッセージは、オーディオ ファイルの形式が正しくないか、MediaPlayer で読み取れないことを示しています。IOExceptioncombineWaveFiles()

私の質問は、誰かが以下のコードで実際の問題を見ているかどうかです (多くの点で最適ではないことはわかっていますが、最初に動作させてからパフォーマンスを心配することを好みます)。

public static String MergeRecordings(String cumulativeFile, String recordFile, int sampleRate, int bpp, int bufferSize, int channels) {
    if (cumulativeFile == null) {
        return recordFile;
    } else if (recordFile == null) {
        return cumulativeFile;
    }

    String outputFile = FileUtils.getFilePath(null, MDSettings.shared().getMainActivity());
    FileUtils.combineWaveFiles(cumulativeFile, recordFile, outputFile, sampleRate, bpp, bufferSize, channels);

    //FileUtils.removeFile(cumulativeFile);
    //FileUtils.removeFile(recordFile);

    return outputFile;
}

//creates a new file containing file1 + file2 stuck together as such.
private static void combineWaveFiles(String file1, String file2, String outputFile, int sampleRate, int bpp, int bufferSize, int channels) {
    FileInputStream in1 = null, in2 = null;
    FileOutputStream out = null;
    long longSampleRate = sampleRate;
    long byteRate = sampleRate * channels * bpp / 8;

    byte[] data;

    try {
        try {
            in1 = new FileInputStream(file1);
        } catch (Exception e) { }

        try {
            in2 = new FileInputStream(file2);
        } catch (FileNotFoundException e) { }

        out = new FileOutputStream(outputFile);

        long file1Size = 0;
        long file2Size = 0;

        if (in1 != null) { file1Size = in1.getChannel().size() - 44; }
        if (in2 != null) { file2Size = in2.getChannel().size() - 44; }

        long totalAudioLen = file1Size + file2Size;
        long totalDataLen = totalAudioLen + 36;

        FileUtils.writeWaveFileHeader(out, totalAudioLen, totalDataLen, longSampleRate, channels, byteRate, bpp);

        if (in1 != null) {
            in1.skip(44);
            data = new byte[bufferSize];

            if (file1Size < bufferSize) {
                data = new byte[(int)file1Size];
            }

            while (in1.read(data) != -1) {
                out.write(data);

                file1Size -= bufferSize;

                if (file1Size <= 0) {
                    break;
                } else if (file1Size < bufferSize) {
                    data = new byte[(int)file1Size];
                }
            }
        }

        if (in2 != null) {
            in2.skip(44);
            data = new byte[bufferSize];

            if (file2Size < bufferSize) {
                data = new byte[(int)file2Size];
            }

            while (in2.read(data) != -1) {
                out.write(data);

                file2Size -= bufferSize;

                if (file2Size <= 0) {
                    break;
                } else if (file2Size < bufferSize) {
                    data = new byte[(int)file2Size];
                }
            }
        }

        out.close();
        if (in1 != null) { in1.close(); }
        if (in2 != null) { in2.close(); }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private static void writeWaveFileHeader(FileOutputStream out, long totalAudioLen, long totalDataLen, long longSampleRate, int channels, long byteRate, int bpp)
throws IOException {

    byte[] header = new byte[44];

    header[0] = 'R';
    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';
    header[13] = 'm';
    header[14] = 't';
    header[15] = ' ';
    header[16] = 16;
    header[17] = 0;
    header[18] = 0;
    header[19] = 0;
    header[20] = 1;
    header[21] = 0;
    header[22] = (byte) channels;
    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)(channels * bpp); //(2 * 16 / 8);
    header[33] = 0;
    header[34] = (byte)bpp;
    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);
}

このコードの大部分は、この回答から取られています。

4

2 に答える 2

6

私はこれを私のAndroidアプリで正確に行います。2 つではなく、ユーザーの選択に基づいて複数のファイルをマージします。AsyncTask を使用して、サンプルをバックグラウンドでマージします。こちらをご覧ください。必要なセクションをフィルタリングするだけです。Sound Recorder + Proと呼ばれる私のアプリに興味がある場合は、マージのほかに、ミックス、エコーの追加、サンプルの増幅を行います。

    @Override
    protected Void doInBackground(Void... params) {

        isProcessingOn=true;
        try {
            DataOutputStream amplifyOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/"  + year +"-"+ month +"-"+ date +"-"+ hour+"-" + min +"-"+ sec+"ME.wav")));
            DataInputStream[] mergeFilesStream = new DataInputStream[selection.size()];
            long[] sizes=new long[selection.size()];
            for(int i=0; i<selection.size(); i++) {
                File file = new File(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/" +selection.get(i));
             sizes[i] = (file.length()-44)/2;
            }
            for(int i =0; i<selection.size(); i++) {
                mergeFilesStream[i] =new DataInputStream(new BufferedInputStream(new FileInputStream(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/" +selection.get(i))));

                if(i == selection.size()-1) {
                    mergeFilesStream[i].skip(24);
                    byte[] sampleRt = new byte[4];
                    mergeFilesStream[i].read(sampleRt);
                    ByteBuffer bbInt = ByteBuffer.wrap(sampleRt).order(ByteOrder.LITTLE_ENDIAN);
                    RECORDER_SAMPLERATE = bbInt.getInt();
                    mergeFilesStream[i].skip(16);
                }
                    else {
                        mergeFilesStream[i].skip(44);
                    }

            }

            for(int b=0; b<selection.size(); b++) {
            for(int i=0; i<(int)sizes[b]; i++) {
                 byte[] dataBytes = new byte[2];
                 try {
                 dataBytes[0] = mergeFilesStream[b].readByte();
                 dataBytes[1] = mergeFilesStream[b].readByte();
                 }
                 catch (EOFException e) {
                    amplifyOutputStream.close();
                 }
                 short dataInShort = ByteBuffer.wrap(dataBytes).order(ByteOrder.LITTLE_ENDIAN).getShort();
                 float dataInFloat= (float) dataInShort/37268.0f;


                short outputSample = (short)(dataInFloat * 37268.0f);
                byte[] dataFin = new byte[2];
               dataFin[0] = (byte) (outputSample & 0xff);
               dataFin[1] = (byte)((outputSample >> 8) & 0xff);        
              amplifyOutputStream.write(dataFin, 0 , 2);

            }
            }
            amplifyOutputStream.close();
            for(int i=0; i<selection.size(); i++) {
                mergeFilesStream[i].close();
            }

        } catch (FileNotFoundException e) {

            e.printStackTrace();
        } catch (IOException e) {

            e.printStackTrace();
        }
        long size =0;
        try {
            FileInputStream fileSize = new FileInputStream(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/"+year +"-"+ month +"-"+ date +"-"+ hour+"-" + min +"-"+ sec+"ME.wav");
            size = fileSize.getChannel().size();
            fileSize.close();
        } catch (FileNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        final int RECORDER_BPP = 16;

        long datasize=size+36;
        long byteRate = (RECORDER_BPP * RECORDER_SAMPLERATE)/8;
        long longSampleRate = RECORDER_SAMPLERATE;
        byte[] header = new byte[44];


         header[0] = 'R';  // RIFF/WAVE header 
         header[1] = 'I';
         header[2] = 'F';
         header[3] = 'F';
         header[4] = (byte) (datasize & 0xff);
         header[5] = (byte) ((datasize >> 8) & 0xff);
         header[6] = (byte) ((datasize >> 16) & 0xff);
         header[7] = (byte) ((datasize >> 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) 1;
         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) ((RECORDER_BPP) / 8);  // block align
         header[33] = 0;
         header[34] = RECORDER_BPP;  // bits per sample
         header[35] = 0;
         header[36] = 'd';
         header[37] = 'a';
         header[38] = 't';
         header[39] = 'a';
         header[40] = (byte) (size & 0xff);
         header[41] = (byte) ((size >> 8) & 0xff);
         header[42] = (byte) ((size >> 16) & 0xff);
         header[43] = (byte) ((size >> 24) & 0xff);
       // out.write(header, 0, 44); 

        try {
             RandomAccessFile rFile = new RandomAccessFile(Environment.getExternalStorageDirectory() + "/Soundrecpluspro/"  +year +"-"+ month +"-"+ date +"-"+ hour+"-" + min +"-"+ sec+ "ME.wav", "rw");
            rFile.seek(0);
            rFile.write(header);
            rFile.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


        return null;
    }
于 2013-10-02T03:48:35.083 に答える
2

wav ファイルを連結するには、次のコードを試してください。

public class ConcateSongActivity extends Activity {
    Button mbutt;
     /** Called when the activity is first created. */
     @Override
     public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
         setContentView(R.layout.main); 
     mbutt = (Button)findViewById(R.id.button_clickme);
     mbutt.setOnClickListener(new OnClickListener() {

         @Override
         public void onClick(View arg0) {

             try {
                 FileInputStream fis1 = new FileInputStream("/sdcard/MJdangerous.wav");
                 FileInputStream fis2 = new FileInputStream("/sdcard/MJBad.wav");
                 SequenceInputStream sis = new SequenceInputStream(fis1,fis2);


                 FileOutputStream fos = new FileOutputStream(new File("/sdcard/MJdangerousMJBad.wav"));

                 int temp;

                 try {
                     while ((temp = sis.read())!= -1){

                         fos.write(temp);

                     }
                 } catch (IOException e) {
                     // TODO Auto-generated catch block
                     e.printStackTrace();
                 }




             } catch (FileNotFoundException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
            }


         }
     });
 } }

注: このパーミッション WRITE_EXTERNAL_STORAGE を付与することを忘れないでください。

その他のトリック: これは、3 つのサウンドをミックスするために使用される方法です。

private void mixFiles(){
try {
InputStream is1 = getResources().openRawResource(R.raw.test1);
List<Short> music1 = createMusicArray(is1);
InputStream is2 = getResources().openRawResource(R.raw.test2);
List<Short> music2 = createMusicArray(is2);

completeStreams(music1,music2);
short[] music1Array = buildShortArray(music1);
short[] music2Array = buildShortArray(music2);

short[] output = new short[music1Array.length];
for(int i=0; i < output.length; i++){
float samplef1 = music1Array[i] / 32768.0f;
float samplef2 = music2Array[i] / 32768.0f;

float mixed = samplef1 + samplef2; 
// reduce the volume a bit:
mixed *= 0.8;
// hard clipping
if (mixed > 1.0f) mixed = 1.0f;
if (mixed < -1.0f) mixed = -1.0f;
short outputSample = (short)(mixed * 32768.0f);
output[i] = outputSample;
}
saveToFile(output);
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

PCM ミックス サウンドができたら、すべてのプレイヤーが読めるように .wav ファイルに変換できます。

      /**
         * Dealing with big endian streams
         * @param byte0
         * @param byte1
         * @return a shrt with the two bytes swapped
         */
        private static short swapBytes(byte byte0, byte byte1){
            return (short)((byte1 & 0xff) << 8 | (byte0 & 0xff));
        }

        /**
         * From file to byte[] array
         * @param sample 
         * @param swap should swap bytes?
         * @return
         * @throws IOException
         */
        public static byte[] sampleToByteArray(File sample, boolean swap) throws IOException{
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sample));
            int BUFFERSIZE = 4096;
            byte[] buffer = new byte[BUFFERSIZE];
            while(bis.read(buffer) != - 1){
                baos.write(buffer);
            }
            byte[] outputByteArray = baos.toByteArray();
            bis.close();
            baos.close();

            if(swap){
                for(int i=0; i < outputByteArray.length - 1; i=i+2){
                    byte byte0 = outputByteArray[i];
                    outputByteArray[i] = outputByteArray[i+1];
                    outputByteArray[i+1] = byte0;
                }
            }

            return outputByteArray;
        }

        /**
         * Read a file and returns its contents as array of shorts
         * @param sample the sample file
         * @param swap true if we should swap the bytes of short (reading a little-endian file), false otherwise (reading a big-endian file)
         * @return
         * @throws IOException
         */
        public static short[] sampleToShortArray(File sample, boolean swap) throws IOException{
            short[] outputArray = new short[(int)sample.length()/2];


            byte[] outputByteArray = sampleToByteArray(sample,false);


            for(int i=0, j=0; i < outputByteArray.length; i+= 2, j++){
                if(swap){
                    outputArray[j] = swapBytes(outputByteArray[i], outputByteArray[i + 1]);
                }
                else{
                    outputArray[j] = swapBytes(outputByteArray[i + 1], outputByteArray[i]);
                }
            }
            return outputArray;
        }

public void completeStreams(List<Short> mListShort_1, List<Short> mListShort_2) {
    //TODO: check length
    int size_a = mListShort_1.size();
    int size_b = mListShort_2.size();

    if (size_a > size_b){
        // adding series of '0'
        for (int i = size_b+1; i <= size_a; i++) {
            mListShort_2.set(i, (short) 0);
        }
    } else if (size_a < size_b) {
        for (int i = size_a+1; i <= size_b; i++) {
            mListShort_1.set(i, (short) 0);
        }
    } else {
        //do nothing
    }
}

クレジット:ナリツィス

于 2013-10-01T09:01:41.687 に答える