Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ripple and voice playback are not synchronized #383

Open
ybrsss opened this issue Dec 17, 2024 · 0 comments
Open

Ripple and voice playback are not synchronized #383

ybrsss opened this issue Dec 17, 2024 · 0 comments

Comments

@ybrsss
Copy link

ybrsss commented Dec 17, 2024

I am developing a chat app using flutter's getx framework. I found that when I receive a voice message and play the audio, the ripple shows that it has been played, but the voice is still playing. When I do not limit the width of the ripple, it can be displayed normally and ends synchronously. Within the fixed width of the conversation bubble, my ripple does not seem to have a way to compress its width to ensure that it can be fully displayed within the conversation bubble.
这是我语音logic中的代码
`
class DmAudioMessageLogic extends GetxController {
String tag = 'DMAudioMessageLogic';
ROLog log;
double maxWidth;
String uid;

DmAudioMessageLogic(
{required this.log, required this.maxWidth, required this.uid});

final DmAudioMessageState state = DmAudioMessageState();
final PlayerController playerController = PlayerController();
late StreamSubscription playerStateSubscription;
List waveformData = [];
late PlayerWaveStyle playerWaveStyle;

@OverRide
void onInit() {
super.onInit();
playerWaveStyle = PlayerWaveStyle(
fixedWaveColor: const Color(0xFFD8D8D8),
liveWaveColor: log.incoming
? DMColor.textColorPrimary
: DMColor.btnColorPrimary,
spacing: 3,
waveThickness: 2);
initDate(); // 初始化数据
autoDownLoad();
playerStateSubscription =
playerController.onPlayerStateChanged.listen((onData) {
DMLogHelper.logInfo(tag, "${playerController.playerKey}--状态更新--$onData");
if (onData == PlayerState.paused) {
state.isPlaying.value = false;
}
});
}

/// 下载语音
Future autoDownLoad() async {
File file = File(log.fileUrl!.absoluteFilePath!);
if (!await file.exists()) {
DMLogHelper.logInfo(tag, "文件不存在");
String signedUrl = log.fileUrl!.url!.signedUrl;
String savePath = log.fileUrl!.absoluteFilePath!;
await DMHttpHelper().dio.download(signedUrl, savePath,
onReceiveProgress: (count, total) {
if (total == count) {
state.isDownLoaded.value = true;
} else {
state.downLoadProgress.value = count / total;
}
});
}
if (await file.exists()) {
state.isDownLoaded.value = true;
await preparePlayer(file);
}
}

/// 准备播放器
Future preparePlayer(File file) async {
DMLogHelper.logInfo(tag, "准备播放器");

final width = getWidth();
// final samples = (width ~/ 8).clamp(50, 200);
final Samples =  playerWaveStyle.getSamplesForWidth(width);
print("width---$width------samples--${Samples}");
await playerController.preparePlayer(
  path: file.path,
  shouldExtractWaveform: false,
  noOfSamples: Samples,
  // noOfSamples: width ~/ 3,
  volume: 1.0,
);

}

/// 从 .dat 文件中提取波纹
initDate() {
try {
File file = File(log.fileUrl!.absolutePreviewFilePath!);
String jsonString = file.readAsStringSync();
DMLogHelper.logInfo(tag, 'json string $jsonString');
List decodedList = json.decode(jsonString);
waveformData =
decodedList.map((dynamic item) => (item as double) / 10.0).toList();
} catch (error) {
DMLogHelper.logError(tag, "读取波形数据失败: $error");
}
final duration = Duration(seconds: log.fileUrl!.duration!.round());
final minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0');
final remainingSeconds =
duration.inSeconds.remainder(60).toString().padLeft(2, '0');
state.audioTime.value = '${minutes}:${remainingSeconds}';
}

/// 播放语音
playAudio() async {
DMLogHelper.logInfo(tag, "处理语音--${(playerController.playerState)}");

// 正在播放
if (playerController.playerState == PlayerState.playing) {
  state.isPlaying.value = false;
  await playerController.pausePlayer(); //暂停播放
  await DMAudioPlayerManager.instance.pausePlaying(log.imdnId);
}
// 暂停/停止/初始化
else if (playerController.playerState == PlayerState.paused ||
    playerController.playerState == PlayerState.stopped ||
    playerController.playerState == PlayerState.initialized) {
  state.isPlaying.value = true;
  await DMAudioPlayerManager.instance
      .startPlaying(log.imdnId, playerController);
  playerController.setFinishMode(finishMode: FinishMode.pause);

  DmImLogic logic = Get.find<DmImLogic>(tag: uid);

  if (logic.recorderState() == RecorderState.recording) {
    logic.pauseRecordWithUI();
  }

  // 修改语音播放状态
  if (!log.voiceIsPlayed) {
    await DMDbHelper.beginTransaction(() {
      log.voiceIsPlayed = true;
    });
  }
}

}

/// 音频波形的实际宽度
/// 0-29s 宽度自适应 minWidth = 62,音频波形宽度 = 62+(录制时长-1)*4
/// 30-59s 178
/// >60 气泡最大宽度-74
double getWidth() {
final duration = log.fileUrl?.duration ?? 0;
if (duration < 30) {
return math.max(62, 62 + (duration * 4));
} else if (duration < 60) {
return 178;
} else {
return maxWidth - 74;
}
}

/// 处理发送按钮
void dealSendIcon() {
if (log.state == DMConstants.messageStateSendOk) {
state.isSendOver.value = true;
} else if (log.state == DMConstants.messageStateSendFail) {
state.isSendOver.value = true;
}
}
}
这是我语音气泡的widget viewclass DMAudioMessagePage extends StatelessWidget {
double maxWidth = 0.0;
ROLog log;
BorderRadius borderRadius;

DMAudioMessagePage(
{super.key,
required this.maxWidth,
required this.borderRadius,
required this.log});

@OverRide
Widget build(BuildContext context) {
double minWidth = 136;
double minHeight = 68;
return GetBuilder(
tag: log.imdnId,
builder: (logic) {
return GestureDetector(
onTap: () => logic.playAudio(),
child: Padding(
padding: EdgeInsets.only(left: log.incoming ? 16 : 0),
child: IntrinsicWidth(
child: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: maxWidth, minHeight: minHeight),
child: Container(
constraints: BoxConstraints(minWidth: minWidth),
decoration: BoxDecoration(
color: log.incoming
? messageBubbleBackground(context)
: Theme.of(context).primaryColor,
borderRadius: borderRadius,
),
padding: const EdgeInsets.symmetric(
vertical: 11, horizontal: 12),
child: IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Obx(() {
return buildIcon(logic);
}),
const SizedBox(width: 10),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 音频条
AudioFileWaveforms(
size: Size(logic.getWidth(), 24.0),
// 提取数据后立即显示波形
waveformData: logic.waveformData,
playerController: logic.playerController,
// 波形类型
waveformType: WaveformType.fitWidth,
playerWaveStyle: logic.playerWaveStyle,
),
// 时长条
const SizedBox(height: 4),
audioTime(logic),
],
)
],
),
),
),
),
),
),
);
});
}这是在聊天界面中使用它的部分 /// 语音消息
Widget buildAudioMessage(
ROLog log, DmImLogic logic, BorderRadius borderRadius) {
double maxWidth = sDeviceInfo.idiom != 'pad'
? logic.state.currentViewWidth * 0.725
: logic.state.currentViewWidth * 0.5625;
Get.put(DmAudioMessageLogic(log: log, maxWidth: maxWidth, uid: uid),
tag: log.imdnId);
return GetBuilder(
tag: log.imdnId,
builder: (logic) {
return DMAudioMessagePage(
key: ValueKey(log.imdnId),
borderRadius: borderRadius,
log: log,
maxWidth: maxWidth,
);
});
}`
我的波纹数据是从预览文件中提取出来的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant