package com.weishitechsub.quanminchangKmianfei.fragment.lilv;
import android.Manifest;
import android.content.Intent;
import android.graphics.Color;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.SeekBar;
import androidx.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.czt.mp3recorder.Mp3Recorder;
import com.czt.mp3recorder.Mp3RecorderUtil;
import com.hfd.common.util.DensityUtil;
import com.hfd.common.util.ToastUtil;
import com.hw.lrcviewlib.ILrcViewSeekListener;
import com.hw.lrcviewlib.LrcRow;
import com.weishitechsub.quanminchangKmianfei.AppAdEnum;
import com.weishitechsub.quanminchangKmianfei.LCAppcation;
import com.weishitechsub.quanminchangKmianfei.R;
import com.weishitechsub.quanminchangKmianfei.adtakubase.inter.insert.IAdInsertView;
import com.weishitechsub.quanminchangKmianfei.adtakubase.inter.nativ.IAdNativeView;
import com.weishitechsub.quanminchangKmianfei.bean.CloseSong;
import com.weishitechsub.quanminchangKmianfei.bean.ReRecord;
import com.weishitechsub.quanminchangKmianfei.bean.SongInfo;
import com.weishitechsub.quanminchangKmianfei.dialog.CommonDialog;
import com.weishitechsub.quanminchangKmianfei.dialog.PermissionDialog;
import com.weishitechsub.quanminchangKmianfei.dialog.SetSoundDialog;
import com.weishitechsub.quanminchangKmianfei.adtakubase.activity.BaseBindActivity;
import com.weishitechsub.quanminchangKmianfei.databinding.ActivitySingBinding;
import com.weishitechsub.quanminchangKmianfei.utils.DisplayUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import pub.devrel.easypermissions.EasyPermissions;
public class SingActivity extends BaseBindActivity<ActivitySingBinding> implements EasyPermissions.PermissionCallbacks, IAdInsertView, IAdNativeView {
private MediaPlayer mediaPlayer;//播音
private int recordTime = 0;//录制时间
private static final String[] PERMS_CAMERA = new String[] {Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.RECORD_AUDIO}; // 需要请求的权限数组
private static final int RC_CAMERA_PERM = 123; // 请求码,唯一标识
Mp3Recorder mRecorder;//录音
List<LrcRow> lrcRows;
long getCurrentTime = 0;
private static final int PERMISSION_REQUEST_CODE = 123;
private String lrcText;
private SongInfo songInfo;
private boolean isMuteMode = false; // 是否静音原唱(只录人声)
private boolean hasClickedTvType = false; // 是否点击过 tvType
@Override
protected void init() {
Intent intent = getIntent();
if (intent != null && intent.hasExtra("song_data")) {
Serializable serializable = intent.getSerializableExtra("song_data");
if (!(serializable instanceof SongInfo)) {
ToastUtil.showShortToast("无效的歌曲数据");
finish();
return;
}
songInfo = (SongInfo) serializable;
// 使用统一接口获取数据
Glide.with(this)
.load(songInfo.getCover())
.transform(new RoundedCorners(DisplayUtils.dp2px(LCAppcation.getInstance(), 10f)))
.into(mBinding.ivTopIcon);
mBinding.tvTitle.setText(songInfo.getTitle());
mBinding.tvTopSongName.setText(songInfo.getTitle());
mBinding.tvTopSingName.setText(songInfo.getSinger());
mBinding.tvTimeEnd.setText(songInfo.getSongTime());
// 设置音乐播放器
String musicUrl = songInfo.getMusic(); // 可能是网络地址 or 本地路径
mediaPlayer = createMediaPlayer(musicUrl);
if (mediaPlayer == null) {
ToastUtil.showShortToast("无法加载音频文件");
}
// 设置歌词
lrcText = songInfo.getLrc();
lrcRows = new LrcDataBuilder().builtFromText(lrcText);
mBinding.lyricView.setLrcData(lrcRows);
getCurrentTime = System.currentTimeMillis();
setOnClick();
} else {
ToastUtil.showShortToast("未收到歌曲信息");
finish();
}
initInsert(AppAdEnum.AD_INTERSTITIAL_kg);
initNative(AppAdEnum.AD_NATIVE_KG);
}
private void setOnClick() {
lrcRows = new LrcDataBuilder().builtFromText(lrcText);
mBinding.lyricView.getLrcSetting()
.setTimeTextSize(40)//时间字体大小
.setSelectLineColor(Color.parseColor("#ff0000"))//选中线颜色
.setSelectLineTextSize(25)//选中线大小
.setNormalRowColor(Color.parseColor("#333333"))
.setHeightRowColor(Color.parseColor("#ff0000"))//高亮字体颜色
.setNormalRowTextSize(DensityUtil.sp2px(SingActivity.this, 17))//正常行字体大小
.setHeightLightRowTextSize(DensityUtil.sp2px(SingActivity.this, 17))//高亮行字体大小
.setTrySelectRowTextSize(DensityUtil.sp2px(SingActivity.this, 17))//尝试选中行字体大小
.setTimeTextColor(Color.parseColor("#ff0000"))//时间字体颜色
.setTrySelectRowColor(Color.parseColor("#ff0000"));//尝试选中字体颜色
mBinding.lyricView.commitLrcSettings();
mBinding.lyricView.setLrcData(lrcRows);
mBinding.lyricView.setLrcViewSeekListener(new ILrcViewSeekListener() {
@Override
public void onSeek(LrcRow currentLrcRow, long CurrentSelectedRowTime) {
mediaPlayer.seekTo((int) CurrentSelectedRowTime); // 跳转到指定时间
}
});
Mp3RecorderUtil.init(SingActivity.this ,false);
EventBus.getDefault().register(this);
mBinding.tvBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mBinding.lineUtil.getVisibility() == View.VISIBLE && !mBinding.tvSing.getText().toString().equals("开始演唱")){
mBinding.tvSing.setText("暂停");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_ht);
if(null != mediaPlayer){
mediaPlayer.pause();
}
if(null != mRecorder){
mRecorder.pause();
}
CommonDialog commonDialog = new CommonDialog(SingActivity.this,"确定退出录制并放弃录制歌曲?");
commonDialog.show();
commonDialog.setOnDialogClickListener(new CommonDialog.OnDialogClickListener() {
@Override
public void onSureClickListener() {
commonDialog.dismiss();
finish();
}
@Override
public void onQuitClickListener() {
commonDialog.dismiss();
}
});
}else {
finish();
}
}
});
//切换伴奏
mBinding.tvType.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mediaPlayer == null) return;
if (isMuteMode) {
// 当前是静音状态 → 恢复原唱
mediaPlayer.setVolume(1.0f, 1.0f); // 左右声道恢复正常音量
ToastUtil.showShortToast("已恢复原唱");
} else {
// 当前是播放状态 → 静音原唱
mediaPlayer.setVolume(0.0f, 0.0f); // 静音
ToastUtil.showShortToast("已进入纯人声录制模式");
}
isMuteMode = !isMuteMode; // 切换状态
//标记:用户已经点击过 tvType
hasClickedTvType = true;
}
});
//调音
mBinding.tvVoice.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new SetSoundDialog(SingActivity.this).show();
}
});
//开始录音
mBinding.line.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mBinding.tvSing.getText().toString().equals("开始演唱")){
getPermissions();
}else if(mBinding.tvSing.getText().toString().equals("正在录制")){
mBinding.tvSing.setText("暂停");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_ht);
mediaPlayer.pause();
mRecorder.pause();
}else if(mBinding.tvSing.getText().toString().equals("暂停")){
mBinding.tvSing.setText("正在录制");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_lz);
mediaPlayer.start();
mRecorder.start();
}else {
}
}
});
//重新录制
mBinding.tvReRecord.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mBinding.tvSing.getText().toString().equals("开始演唱")){
ToastUtil.showShortToast("你还没有开始录制呢");
}else{
// 在重新录制逻辑中
mBinding.tvTimeStart.setText("00:00");
mBinding.tvSing.setText("暂停");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_ht);
mediaPlayer.pause();
mRecorder.pause();
CommonDialog commonDialog = new CommonDialog(SingActivity.this,"重录后当前数据会被删除,是否重录");
commonDialog.show();
commonDialog.setOnDialogClickListener(new CommonDialog.OnDialogClickListener() {
@Override
public void onSureClickListener() {
mBinding.seekBar.setProgress(0);
mediaPlayer.seekTo(0);
mBinding.lyricView.smoothScrollToTime(0);
mBinding.tvSing.setText("开始演唱");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_lz);
recordTime = 0;
mBinding.tvTimeStart.setText("00:00");
mRecorder.stop(Mp3Recorder.ACTION_STOP_ONLY);
commonDialog.dismiss();
}
@Override
public void onQuitClickListener() {
commonDialog.dismiss();
}
});
}
}
});
//录制完成
mBinding.tvEnd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mBinding.tvSing.getText().toString().equals("开始演唱")){
ToastUtil.showShortToast("你还没有开始录制呢");
}else{
mBinding.tvSing.setText("暂停");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_ht);
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
try {
mediaPlayer.pause();
} catch (IllegalStateException e) {
Log.w("MediaPlayer", "pause failed: not in correct state");
}
}
mRecorder.pause();
CommonDialog commonDialog = new CommonDialog(SingActivity.this,"作品录制最少20秒,是否完成录制");
commonDialog.show();
commonDialog.setOnDialogClickListener(new CommonDialog.OnDialogClickListener() {
@Override
public void onSureClickListener() {
if(recordTime < 20){
ToastUtil.showShortToast("作品录制最少20秒");
}else{
mediaPlayer.pause();
mRecorder.stop(Mp3Recorder.ACTION_STOP_ONLY);
Bundle bundle = new Bundle();
bundle.putString("recorded_audio_path", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/" + getCurrentTime + songInfo.getTitle());
bundle.putSerializable("song_data", songInfo); // 原歌曲信息也传过去
//根据是否点击过 tvType 设置 type
int type = hasClickedTvType ? 0 : 1;
bundle.putInt("type", type);
toClass(SingEndActivity.class, bundle);
}
commonDialog.dismiss();
}
@Override
public void onQuitClickListener() {
commonDialog.dismiss();
}
});
}
}
});
}
/**
* 根据音乐 URL 创建并准备 MediaPlayer
* 支持:网络地址(http/https)、本地文件路径
*
* @param musicUrl 音乐文件的路径或 URL
* @return 准备好的 MediaPlayer 实例,失败返回 null
*/
private MediaPlayer createMediaPlayer(String musicUrl) {
MediaPlayer mediaPlayer = new MediaPlayer();
try {
// 判断是否是网络地址(以 http 或 https 开头)
if (musicUrl.startsWith("http://") || musicUrl.startsWith("https://")) {
// 使用 setDataSource(String) 直接传入网络地址
mediaPlayer.setDataSource(musicUrl);
} else {
// 否则视为本地文件路径(file:/// 或普通路径)
File file = new File(musicUrl);
if (file.exists()) {
mediaPlayer.setDataSource(file.getAbsolutePath());
} else {
Log.e("MediaPlayer", "文件不存在: " + musicUrl);
return null;
}
}
// 异步准备(推荐用于网络资源,避免阻塞主线程)
mediaPlayer.prepareAsync();
// 设置一个准备完成监听器
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mBinding.seekBar.setMax(mp.getDuration());
//在这里注册 SeekBar 的监听器,确保 MediaPlayer 已准备好
setupSeekBarListener();
Log.d("MediaPlayer", "音频已准备就绪,时长: " + millisecondsToString(mp.getDuration()));
}
});
// 错误监听器(非常重要!防止无声崩溃)
mediaPlayer.setOnErrorListener((mp, what, extra) -> {
Log.e("MediaPlayer", "播放错误:what=" + what + ", extra=" + extra);
ToastUtil.showShortToast("播放失败,错误码:" + what);
return true; // 返回 true 表示我们处理了错误
});
return mediaPlayer;
} catch (Exception e) {
Log.e("MediaPlayer", "创建 MediaPlayer 失败", e);
ToastUtil.showShortToast("无法播放该音频:" + e.getMessage());
if (mediaPlayer != null) {
try {
mediaPlayer.release();
} catch (Exception ignored) {}
}
return null;
}
}
private void setupSeekBarListener() {
mBinding.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
Log.d("SeekBar", "Progress changed: " + progress + ", fromUser=" + fromUser);
if (fromUser && mediaPlayer != null) {
Log.d("MediaPlayer", "Calling seekTo(" + progress + ")");
try {
mediaPlayer.seekTo(progress);
} catch (IllegalStateException e) {
Log.e("MediaPlayer", "seekTo failed: not prepared", e);
}
}
// 更新 UI 时间
mBinding.tvTimeStart.setText(millisecondsToString(progress));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.pause();
}
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
if (mediaPlayer != null) {
mediaPlayer.start();
mBinding.lyricView.smoothScrollToTime(mediaPlayer.getCurrentPosition());
}
}
});
}
// 将格式化的时间字符串(00:00)转换为毫秒
private int timeToMilliseconds(String time) {
if (time == null || time.isEmpty()) return 0;
String[] parts = time.split(":");
try {
int minutes = Integer.parseInt(parts[0]);
int seconds = Integer.parseInt(parts[1]);
return (minutes * 60 + seconds) * 1000;
} catch (Exception e) {
return 0;
}
}
// 将毫秒转换为格式化的时间字符串(00:00)
private String millisecondsToString(int milliseconds) {
int seconds = (milliseconds / 1000) % 60;
int minutes = (milliseconds / (1000 * 60)) % 60;
return String.format("%02d:%02d", minutes, seconds);
}
// 自定义的线程
private Handler progressHandler = new Handler(Looper.getMainLooper());
private Runnable updateProgressRunnable;
private void startUpdateProgress() {
stopUpdateProgress(); // 防止重复启动
updateProgressRunnable = new Runnable() {
@Override
public void run() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
int currentPosition = mediaPlayer.getCurrentPosition();
// 更新 SeekBar 和时间文本
mBinding.seekBar.setProgress(currentPosition);
mBinding.tvTimeStart.setText(millisecondsToString(currentPosition));
mBinding.textViewProgress.setText(millisecondsToString(currentPosition));
// 更新歌词滚动
mBinding.lyricView.smoothScrollToTime(currentPosition);
}
// 每 500ms 更新一次(比 1s 更平滑)
progressHandler.postDelayed(this, 500);
}
};
progressHandler.post(updateProgressRunnable);
}
private void stopUpdateProgress() {
if (updateProgressRunnable != null) {
progressHandler.removeCallbacks(updateProgressRunnable);
}
}
/**
* 检查并请求权限
*/
private void getPermissions(){
if (!EasyPermissions.hasPermissions(SingActivity.this, PERMS_CAMERA)) {
new PermissionDialog(SingActivity.this, "权限说明: 存储权限:获取该存储权限主要是为了读写音频资源文件。录音权限:获取该录音权限主要为了录制您的声音便于后期音乐合成。若拒绝将无法使用唱K功能").show();
EasyPermissions.requestPermissions(SingActivity.this, "我们需要存储权限和录音权限", RC_CAMERA_PERM, PERMS_CAMERA);
} else {
if (mediaPlayer != null) {
if (mRecorder == null) {
mRecorder = new Mp3Recorder();
mRecorder.setOutputFile(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) +
"/" + getCurrentTime + "_" + songInfo.getTitle() + ".mp3"
).setMaxDuration(0); // 0 表示不限时长
}
// 重置状态
mediaPlayer.seekTo(0);
mBinding.seekBar.setProgress(0);
mBinding.lyricView.smoothScrollToTime(0);
mRecorder.start();
mediaPlayer.start();
mBinding.tvSing.setText("正在录制");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_lz);
recordTime = 0;
startUpdateProgress(); // 启动进度更新
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 把执行结果的操作给EasyPermissions
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
if(requestCode == RC_CAMERA_PERM){
getPermissions();
}
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
}
// 使用@Subscribe注解标记方法以监听事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(ReRecord reRecord) {
//重新录制
mBinding.seekBar.setProgress(0);
mediaPlayer.seekTo(0);
mBinding.lyricView.smoothScrollToTime(0);
mBinding.tvSing.setText("开始演唱");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_lz);
recordTime = 0;
mBinding.tvTimeStart.setText("00:00");
}
// 使用@Subscribe注解标记方法以监听事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(CloseSong song) {
finish();
}
// 内部歌词解析器(保持不变)
public static class LrcDataBuilder { // 加上 static
public List<LrcRow> builtFromText(String lrcText) {
List<LrcRow> rows = new ArrayList<>();
if (lrcText == null || lrcText.trim().isEmpty()) {
return rows;
}
String[] lines = lrcText.split("\\r?\\n");
for (String line : lines) {
line = line.trim();
if (line.isEmpty()) continue;
List<LrcRow> parsed = parseLrcLine(line);
if (!parsed.isEmpty()) {
rows.addAll(parsed);
}
}
Collections.sort(rows);
return rows;
}
private List<LrcRow> parseLrcLine(String line) {
List<LrcRow> result = new ArrayList<>();
if (line.isEmpty()) return result;
// 支持多种换行符预处理(避免后续污染)
line = line.replaceAll("[\\r\\n\\u2028\\u2029]+", " ");
Pattern pattern = Pattern.compile("\\[(\\d{1,2}):(\\d{2})(?:\\.(\\d{2,3}))?\\]");
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
int min = Integer.parseInt(matcher.group(1));
int sec = Integer.parseInt(matcher.group(2));
int milli = 0;
if (matcher.group(3) != null) {
milli = Integer.parseInt(matcher.group(3));
if (matcher.group(3).length() == 2) milli *= 10; // 补齐毫秒
}
long timeMillis = (min * 60L + sec) * 1000 + milli;
int textStart = matcher.end();
int textEnd = line.length();
// 查找下一个时间标签作为结束点
Matcher nextMatcher = pattern.matcher(line);
if (nextMatcher.find(matcher.start() + 1)) {
textEnd = nextMatcher.start();
}
String rawText = line.substring(textStart, textEnd).trim();
// 再次防御性清理各种空白与特殊字符
String text = rawText
.replaceAll("[\\p{Cc}\\p{Cf}&&[^\\t]]", " ") // 移除控制字符
.replace((char) 0xA0, ' ') // 不间断空格
.replaceAll("\\s+", " ") // 多空格合并
.trim();
if (!text.isEmpty()) {
result.add(new LrcRow(text, matcher.group(0), timeMillis));
}
}
return result;
}
}
@Override
public void onDestroy() {
stopUpdateProgress(); // 停止更新
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
if (mediaPlayer != null) {
mediaPlayer.release();
}
if(null != mRecorder){
mRecorder.reset();
}
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Override
public void onBackPressed() {
if(true){
if(mBinding.lineUtil.getVisibility() == View.VISIBLE && !mBinding.tvSing.getText().toString().equals("开始演唱")){
mBinding.tvSing.setText("暂停");
mBinding.ivSing.setImageResource(R.mipmap.img_sing_ht);
if(null != mediaPlayer){
mediaPlayer.pause();
}
if(null != mRecorder){
mRecorder.pause();
}
CommonDialog commonDialog = new CommonDialog(SingActivity.this,"确定退出录制并放弃录制歌曲?");
commonDialog.show();
commonDialog.setOnDialogClickListener(new CommonDialog.OnDialogClickListener() {
@Override
public void onSureClickListener() {
commonDialog.dismiss();
finish();
}
@Override
public void onQuitClickListener() {
commonDialog.dismiss();
}
});
}else{
super.onBackPressed();
}
}else{
super.onBackPressed();
}
//super.onBackPressed();
}
}这个歌曲是从网络加载来的
最新发布