安卓Android 声音内录,并将pcm文件转成wav文件

1.在AndroidManifest.xml文件中,添加权限

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

并在Application中增加:android:allowAudioPlaybackCapture="true"

2、配置Service

<service
            android:name=".service.MediaProjectService"
            android:foregroundServiceType="mediaProjection" />

3、Service的详细代码,包括声音内录确认框,开始录音,六秒后停止录音,并将录制的pcm文件转换成wav文件

import android.Manifest;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioPlaybackCaptureConfiguration;
import android.media.AudioRecord;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

import androidx.core.app.NotificationCompat;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.LinearLayoutManager;

import com.kgzn.musicrecognition.IdentifyProtocolV1;
import com.kgzn.musicrecognition.MainActivity;
import com.kgzn.musicrecognition.R;
import com.kgzn.musicrecognition.adapter.MusicItemAdapter;
import com.kgzn.musicrecognition.bean.MusicInfo;
import com.kgzn.musicrecognition.constant.ServiceConstant;
import com.kgzn.musicrecognition.dialog.MusicRecognitionDialog;
import com.kgzn.musicrecognition.util.LogUtil;
import com.kgzn.musicrecognition.util.PcmToWavConverter;
import com.kgzn.toastsdk.KgznToast;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author As06898
 * @Description: 声音内录
 */
public class MediaProjectService extends Service implements IdentifyProtocolV1.OnRecognitionListener{
    private static final String TAG = "MediaProjectService";
    private static final int NOTIFICATION_ID = 1;
    private static final String CHANNEL_ID = "MediaProjectionChannel";
    private MediaProjectionManager mediaProjectionManager;
    private MediaProjection mediaProjection;
    private AudioRecord audioRecord;
    private FileOutputStream outputStream;
    private boolean isRecording;
    private Thread recordingThread;
    private String fileName;
    private List<MusicInfo> musicList;
    private MusicRecognitionDialog musicRecognitionDialog;
    private boolean isStop = false;

    @Override
    public void onCreate() {
        super.onCreate();
        musicList = new ArrayList<>();
        mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
        createNotificationChannel();
        startForeground(NOTIFICATION_ID, createNotification());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        LogUtil.d(TAG, "onStartCommand");
        if (intent != null && intent.hasExtra("requestCode") && intent.hasExtra("resultCode") && intent.hasExtra("data")) {
            int requestCode = intent.getIntExtra("requestCode", -1);
            int resultCode = intent.getIntExtra("resultCode", -1);
            Intent data = intent.getParcelableExtra("data");
            handleActivityResult(requestCode, resultCode, data);
        }
        return START_NOT_STICKY;
    }

    private void createNotificationChannel() {
        NotificationChannel serviceChannel = new NotificationChannel(
                CHANNEL_ID,
                "Media Projection Service Channel",
                NotificationManager.IMPORTANCE_DEFAULT
        );
        NotificationManager manager = getSystemService(NotificationManager.class);
        manager.createNotificationChannel(serviceChannel);
    }

    private Notification createNotification() {
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE);

        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Media Projection Service")
                .setContentText("Recording in progress...")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .build();
    }

    private void handleActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == 1234) { // 假设请求码为1234
            startMediaProjection(resultCode, data);
        }
    }
    private void startMediaProjection(int resultCode, Intent data) {
        mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
        startRecording();
    }

    private void startRecording() {
        if (isRecording) {
            return;
        }

        int bufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            AudioPlaybackCaptureConfiguration config = new AudioPlaybackCaptureConfiguration.Builder(mediaProjection)
                    .addMatchingUsage(AudioAttributes.USAGE_MEDIA)
                    .addMatchingUsage(AudioAttributes.USAGE_GAME)
                    .build();
            audioRecord = new AudioRecord.Builder()
                    .setAudioFormat(new AudioFormat.Builder()
                            .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
                            .setSampleRate(44100)
                            .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
                            .build())
                    .setBufferSizeInBytes(bufferSize)
                    .setAudioPlaybackCaptureConfig(config)
                    .build();
        }

        try {
            String uuid = UUID.randomUUID().toString();
            fileName = "audio_" + uuid;
            File file = new File(getExternalFilesDir(null), fileName + ".pcm");
            outputStream = new FileOutputStream(file);
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }

        isRecording = true;

        recordingThread = new Thread(new Runnable() {
            @Override
            public void run() {
                byte[] buffer = new byte[bufferSize];
                while (isRecording) {
                    int readSize = audioRecord.read(buffer, 0, bufferSize);
                    if (readSize > 0) {
                        try {
                            outputStream.write(buffer, 0, readSize);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }
                audioRecord.stop();
                audioRecord.release();
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        audioRecord.startRecording();
        recordingThread.start();

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                stopRecording();
            }
        }, 6000);
    }

    private void stopRecording() {
        if (!isRecording) {
            return;
        }

        isRecording = false;
        try {
            recordingThread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        File pcmFile = new File(getExternalFilesDir(null), fileName + ".pcm");
        File wavFile = new File(getExternalFilesDir(null), fileName + ".wav");

        try {
            if(!ServiceConstant.SERVICE_STOP){
                PcmToWavConverter.convertPcmToWav(pcmFile, wavFile, 44100, 1, 16);

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

        stopSelf();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

4、在MainActivity的onActivityResult方法中获取requestCode和resultCode,并启动服务。

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE) {
            if (resultCode == Activity.RESULT_OK) {
                startMediaProjectService(requestCode, resultCode, data);
            } else {
//                Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show();
//                finish(); // 关闭当前 Activity
                finishAffinity(); // 关闭所有相关的 Activity
                System.exit(0); // 强制退出应用
            }
        }
    }

    private void startMediaProjectService(int requestCode, int resultCode, Intent data) {
        Intent serviceIntent = new Intent(this, MediaProjectService.class);
        serviceIntent.putExtra("requestCode", requestCode);
        serviceIntent.putExtra("resultCode", resultCode);
        serviceIntent.putExtra("data", data);
        startService(serviceIntent);
    }

5、将pcm音频文件转为wav文件的工具类


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class PcmToWavConverter {

    public static void convertPcmToWav(File pcmFile, File wavFile, int sampleRate, int channelCount, int bitDepth) throws IOException {
        byte[] header = generateWavHeader(sampleRate, channelCount, bitDepth, (int) pcmFile.length());
        FileInputStream in = new FileInputStream(pcmFile);
        FileOutputStream out = new FileOutputStream(wavFile);

        // Write the WAV header
        out.write(header, 0, header.length);

        // Copy the PCM data
        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1) {
            out.write(buffer, 0, read);
        }

        in.close();
        out.close();
    }

    private static byte[] generateWavHeader(int sampleRate, int channelCount, int bitDepth, int dataSize) {
        int byteRate = sampleRate * channelCount * (bitDepth / 8);
        long totalDataSize = 36 + dataSize;
        byte[] header = new byte[44];

        // RIFF chunk descriptor
        header[0] = 'R';  // RIFF/WAVE header
        header[1] = 'I';
        header[2] = 'F';
        header[3] = 'F';
        header[4] = (byte) (totalDataSize & 0xff);
        header[5] = (byte) ((totalDataSize >> 8) & 0xff);
        header[6] = (byte) ((totalDataSize >> 16) & 0xff);
        header[7] = (byte) ((totalDataSize >> 24) & 0xff);
        header[8] = 'W';
        header[9] = 'A';
        header[10] = 'V';
        header[11] = 'E';

        // FMT sub-chunk
        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) channelCount;
        header[23] = 0;
        header[24] = (byte) (sampleRate & 0xff);
        header[25] = (byte) ((sampleRate >> 8) & 0xff);
        header[26] = (byte) ((sampleRate >> 16) & 0xff);
        header[27] = (byte) ((sampleRate >> 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 * channelCount);  // block align
        header[33] = 0;
        header[34] = (byte) bitDepth;  // bits per sample
        header[35] = 0;

        // Data sub-chunk
        header[36] = 'd';
        header[37] = 'a';
        header[38] = 't';
        header[39] = 'a';
        header[40] = (byte) (dataSize & 0xff);
        header[41] = (byte) ((dataSize >> 8) & 0xff);
        header[42] = (byte) ((dataSize >> 16) & 0xff);
        header[43] = (byte) ((dataSize >> 24) & 0xff);

        return header;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值