newventuresoftware / WaveformControl

Android Waveform control
236 stars 67 forks source link

How to load "samples" from a sound file in the local file system? #4

Closed liuzhen2008 closed 7 years ago

liuzhen2008 commented 7 years ago

I try to use the inputFileStream(new File(filePath)) directly on line https://github.com/newventuresoftware/WaveformControl/blob/master/app/src/main/java/com/newventuresoftware/waveformdemo/MainActivity.java#L125 and convert it to byte array. But the resulting image/play back doesn't seem to work properly.

I also used a plain mp3 file as resource. The result is almost a rectangle. Is there any preprocess that needs to be done to the file?

The sound file i am interesting in showing a graph for is created by a MediaRecorder with the following configuration:

    MediaRecorder recorder = new Mediarecorder();
    recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
    recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
    recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
liuzhen2008 commented 7 years ago

Basically i need to convert this 3gp stuff to PCM samples using MediaExtractor/MediaCodec.

yavor87 commented 7 years ago

I reckon it's because of the compression. Currently, the code works only with uncompressed data. The snippet that you shared sets the encoder to AMR (Narrowband) audio codec, which should produce some compression on the resulting stream.

liuzhen2008 commented 7 years ago

@yavor87 Yes thats exactly it. I used mediaCodec to convert the 3gp audio samples into PCM audio samples and the graph worked flawlessly.

irshadshalu commented 5 years ago

@liuzhen2008 Could you share your solution?

liuzhen2008 commented 5 years ago
        try {
            MediaExtractor extractor = new MediaExtractor();
            extractor.setDataSource(filePath);
            extractor.selectTrack(0);
            MediaFormat format = extractor.getTrackFormat(0);
            MediaCodec decoder = MediaCodec.createDecoderByType(format.getString(MediaFormat.KEY_MIME));
            decoder.configure(format, null, null, 0);
            decoder.start();

            ByteBuffer[] inputBuffers = decoder.getInputBuffers();
            ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
            ArrayList<byte[]> dataList = new ArrayList<>();
            int totalLength = 0;
            while (true) {
                int inIndex = decoder.dequeueInputBuffer(5000);
                if (inIndex >= 0) {
                    ByteBuffer buffer = inputBuffers[inIndex];
                    int sampleSize = extractor.readSampleData(buffer, 0);
                    if (sampleSize < 0) {
                        decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                        break;
                    } else {
                        decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
                        extractor.advance();
                    }
                }
                int outIndex = decoder.dequeueOutputBuffer(info, 5000);
                switch (outIndex) {
                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                    case MediaCodec.INFO_TRY_AGAIN_LATER:
                        // whatever
                        break;
                    default:
                        ByteBuffer buffer = outputBuffers[outIndex];
                        int length = info.size - info.offset;
                        totalLength = totalLength + length;
                        int p = buffer.position();
                        byte[] tempBuffer = new byte[length];
                        buffer.get(tempBuffer, 0, length);
                        dataList.add(tempBuffer);
                        buffer.position(p);
                        decoder.releaseOutputBuffer(outIndex, true);
                }
            }
            decoder.stop();
            decoder.release();
            extractor.release();
            byte[] data = new byte[totalLength];
            int offset = 0;
            for (byte[] tempBuffer : dataList) {
                System.arraycopy(tempBuffer, 0, data, offset, tempBuffer.length);
                offset += tempBuffer.length;
            }
            ShortBuffer sb = ByteBuffer.wrap(data, 0, offset)
                    .order(ByteOrder.nativeOrder())
                    .asShortBuffer();
            short[] samples = new short[sb.limit()];
            sb.get(samples);
            updateWaveForm(samples);
        } catch (Throwable e) {
            onRecordingError(e);
        }

@irshadshalu This is what I had. the "samples" at the end is what I passed to the waveformView.setSamples(). "filePath" is the uri string of the 3pg file lcoation

You can google around 3gp to PCM samples, there are a few examples.

irshadshalu commented 5 years ago

Thank you!

mgood7123 commented 5 years ago

so just to be clear, does this accept .wav files or .raw files as input for the waveform to render and/or play