audio_waveforms icon indicating copy to clipboard operation
audio_waveforms copied to clipboard

How to stop D/AudioTrack that working all the time

Open MetaTouchDev opened this issue 9 months ago • 6 comments

D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55205 m:985 s:0 k:0) : pid 4586 uid 10080 sessionId 12681 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55196 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12665 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55203 m:983 s:0 k:0) : pid 4586 uid 10080 sessionId 12625 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55194 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12617 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55176 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12633 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55179 m:956 s:0 k:0) : pid 4586 uid 10080 sessionId 12609 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55177 m:972 s:1 k:0) : pid 4586 uid 10080 sessionId 12593 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55178 m:958 s:0 k:0) : pid 4586 uid 10080 sessionId 12689 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 55s(f:55199 m:968 s:0 k:0) : pid 4586 uid 10080 sessionId 12705 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60196 m:948 s:0 k:0) : pid 4586 uid 10080 sessionId 12513 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60217 m:953 s:0 k:0) : pid 4586 uid 10080 sessionId 12553 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 45s(f:45175 m:983 s:0 k:0) : pid 4586 uid 10080 sessionId 12257 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60256 m:974 s:0 k:0) : pid 4586 uid 10080 sessionId 12657 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60218 m:985 s:0 k:0) : pid 4586 uid 10080 sessionId 12681 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60216 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12665 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60216 m:983 s:0 k:0) : pid 4586 uid 10080 sessionId 12625 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60205 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12617 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60186 m:972 s:1 k:0) : pid 4586 uid 10080 sessionId 12593 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60186 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12633 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60187 m:958 s:0 k:0) : pid 4586 uid 10080 sessionId 12689 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60189 m:956 s:0 k:0) : pid 4586 uid 10080 sessionId 12609 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 60s(f:60216 m:968 s:0 k:0) : pid 4586 uid 10080 sessionId 12705 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5019 m:948 s:0 k:0) : pid 4586 uid 10080 sessionId 12513 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5019 m:953 s:0 k:0) : pid 4586 uid 10080 sessionId 12553 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 50s(f:50198 m:983 s:0 k:0) : pid 4586 uid 10080 sessionId 12257 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5021 m:974 s:0 k:0) : pid 4586 uid 10080 sessionId 12657 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5017 m:985 s:0 k:0) : pid 4586 uid 10080 sessionId 12681 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5017 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12665 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5019 m:983 s:0 k:0) : pid 4586 uid 10080 sessionId 12625 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5018 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12617 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5010 m:972 s:1 k:0) : pid 4586 uid 10080 sessionId 12593 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5011 m:958 s:0 k:0) : pid 4586 uid 10080 sessionId 12689 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5012 m:979 s:0 k:0) : pid 4586 uid 10080 sessionId 12633 sr 16000 ch 1 fmt 1 D/AudioTrack( 4586): [audioTrackData][fine] 5s(f:5010 m:956 s:0 k:0) : pid 4586 uid 10080 sessionId 12609 sr 16000 ch 1 fmt 1

Dear Everyone, Please help me, I want to stop D/AudioTrack when I finish playing voice and It makes my app crash.

MetaTouchDev avatar Mar 13 '25 03:03 MetaTouchDev

@MetaTouchDev Can you please share reproducible code so that we can help you debug the issue?

ujas-m-simformsolutions avatar Mar 18 '25 14:03 ujas-m-simformsolutions

class PlayerWaveWidget extends StatefulWidget {
  String pathVoice = '';

  PlayerWaveWidget({super.key, required this.pathVoice});

  @override
  State<PlayerWaveWidget> createState() => _PlayerWaveWidgetState();
}

class _PlayerWaveWidgetState extends State<PlayerWaveWidget> {
  final PlayerController playerController = PlayerController();
  bool isPlay = false;

  void _playAndPause() async {
    if (playerController.playerState.isPlaying) {
      isPlay = false;
      await playerController.pausePlayer();
    } else {
      isPlay = true;
      await playerController.startPlayer();
      await playerController.setFinishMode(finishMode: FinishMode.pause);
      playerController.onCompletion.listen((onStop) async {
        await playerController.pausePlayer();
        isPlay = false;
        setState(() {});
      });
    }
    // setState(() {});
    // await playerController.preparePlayer(path: widget.pathVoice);
  }

  @override
  void initState() {
    // TODO: implement initState
    initView();
    super.initState();
  }

  @override
  void dispose() {
    // TODO: implement dispose
    playerController.pausePlayer();
    playerController.stopPlayer();
    playerController.pauseAllPlayers();
    playerController.release();
    playerController.dispose();
    super.dispose();
  }

  initView() async {
    // downloadAudioFromUrl(widget.pathVoice);
    try {
      // Get the directory to store the file (ensure to request permissions if needed)
      final directory = await getTemporaryDirectory();
      final filePath =
          '${directory.path}/voice_${DateTime.now().millisecondsSinceEpoch}.mp3';

      // Use http to download the file
      final response = await http.get(Uri.parse(widget.pathVoice));
      if (response.statusCode == 200) {
        // Write the file to the local storage
        final file = File(filePath);
        await file.writeAsBytes(response.bodyBytes);
        // await player.play(DeviceFileSource(filePath));
        await playerController.preparePlayer(
          path: filePath,
          shouldExtractWaveform: true,
          noOfSamples: 80,
        );
      } else {}
    } catch (e) {
      print("Error downloading voice: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(left: 10, bottom: 10),
      child: Row(
        children: [
          Container(
            height: 70,
            decoration: const BoxDecoration(
              borderRadius: BorderRadius.only(
                bottomLeft: Radius.circular(10), // Adjust the radius as needed
                topLeft: Radius.circular(10),
              ),
              color: Colors.blue,
            ),
            child: IconButton(
                onPressed: () async {
                  _playAndPause();
                  setState(() {});
                  playerController.onCompletion.listen((onStop) async {
                    await playerController.pausePlayer();
                    isPlay = false;
                    setState(() {});
                  });
                },
                icon: Icon(
                  isPlay ? Icons.pause_circle : Icons.play_circle,
                  color: Colors.white,
                )),
          ),
          AudioFileWaveforms(
            continuousWaveform: false,
            enableSeekGesture: true,
            size: Size(MediaQuery.of(context).size.width / 1.55, 70),
            playerController: playerController,
            waveformType: WaveformType.fitWidth,
            animationCurve: Curves.easeIn,
            clipBehavior: Clip.none,
            decoration: const BoxDecoration(
              borderRadius: BorderRadius.only(
                bottomRight: Radius.circular(10), // Adjust the radius as needed
                topRight: Radius.circular(10),
              ),
              color: Colors.blue,
            ),
            playerWaveStyle: PlayerWaveStyle(
              fixedWaveColor: Colors.grey.shade300,
              seekLineThickness: 3,
              waveThickness: 1.5,
              spacing: 3,
              liveWaveColor: Colors.white,
              waveCap: StrokeCap.round,
            ),
          ),
        ],
      ),
    );
  }
}

MetaTouchDev avatar Mar 24 '25 03:03 MetaTouchDev

class ChatBubbleScreen extends StatefulWidget {
  const ChatBubbleScreen({super.key});

  @override
  State<ChatBubbleScreen> createState() => _ChatBubbleScreenState();
}

class _ChatBubbleScreenState extends State<ChatBubbleScreen> {
  final ScrollController _scrollController = ScrollController();
  final FocusNode _focusNode = FocusNode();
  final ChatHistoryController chatHistoryController =
      Get.put(ChatHistoryController());
  final TextEditingController _textController = TextEditingController();
  late io.Socket socket;
  late RecorderController recorderController;

  ValueNotifier<bool> _isButtonVisible = ValueNotifier(true);
  ValueNotifier<bool> _isLoading = ValueNotifier(false);

  int page = 1;
  bool isFetchingPreviousPage = false;

  @override
  void initState() {
    super.initState();

    recorderController = RecorderController()
      ..androidEncoder = AndroidEncoder.aac
      ..androidOutputFormat = AndroidOutputFormat.mpeg4
      ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
      ..sampleRate = 16000;

    _focusNode.addListener(_scrollToBottom);
    _fetchChatHistory();

    _scrollController.addListener(_scrollListener);
  }

  void _scrollListener() {
    if (_scrollController.position.pixels ==
            _scrollController.position.minScrollExtent &&
        !isFetchingPreviousPage) {
      _loadPreviousPage();
    }

    bool shouldHideButton = _scrollController.position.pixels ==
        _scrollController.position.maxScrollExtent;
    if (_isButtonVisible.value != !shouldHideButton) {
      _isButtonVisible.value = !shouldHideButton;
    }
  }

  Future<void> _loadPreviousPage() async {
    if (isFetchingPreviousPage) return;
    isFetchingPreviousPage = true;
    _isLoading.value = true;

    await Future.delayed(const Duration(milliseconds: 500)); // Debounce
    page++;
    await _fetchChatHistory();

    _isLoading.value = false;
    isFetchingPreviousPage = false;
  }

  Future<void> _fetchChatHistory() async {
    print("📥 Fetching Chat History for Page: $page");
    await chatHistoryController.getChatHistory(
        uniqueRoom: 'sdfdsf', page: page);
    print("✅ Chat History Fetched");
  }

  void _scrollToBottom() {
    Future.delayed(const Duration(milliseconds: 300), () {
      if (_scrollController.hasClients) {
        _scrollController.animateTo(
          _scrollController.position.maxScrollExtent + 100,
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeInOut,
        );
      }
    });
  }

  @override
  void dispose() {
    _focusNode.removeListener(_scrollToBottom);
    _focusNode.dispose();
    socket.disconnect();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: const CustomAppBar(title: 'Chat App'),
      body: GestureDetector(
        onTap: () => FocusScope.of(context).requestFocus(FocusNode()),
        child: Stack(
          children: [
            Column(
              children: [
                ValueListenableBuilder<bool>(
                  valueListenable: _isLoading,
                  builder: (context, isLoading, child) {
                    return isLoading
                        ? const Center(child: CircularProgressIndicator())
                        : const SizedBox();
                  },
                ),
                Expanded(
                  child: Obx(() {
                    final chatList = chatHistoryController.listChat.reversed
                        .toList(); // Reverse the list for displaying the latest messages at the bottom.
                    return ListView.separated(
                      controller: _scrollController,
                      padding: const EdgeInsets.symmetric(vertical: 10),
                      itemCount: chatList.length,
                      itemBuilder: (context, index) {
                        final chat = chatList[index];
                        final decryptedText = AES256CBC.decryptText(chat.value);

                        String formattedDate = DateFormat('yyyy-MM-dd')
                            .format(DateTime.parse(chat.date));
                        bool showDateChip = index == 0 ||
                            formattedDate !=
                                DateFormat('yyyy-MM-dd').format(
                                    DateTime.parse(chatList[index - 1].date));

                        return Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            if (showDateChip)
                              DateChip(date: DateTime.parse(chat.date)),
                            ChatBubbleItem(
                              chat: chat,
                              decryptedText: decryptedText,
                              isSender: true,
                              recorderController: recorderController,
                            ),
                          ],
                        );
                      },
                      separatorBuilder: (_, __) => const SizedBox(height: 5),
                    );
                  }),
                ),
                ChatInputField(
                  focusNode: _focusNode,
                  textViewController: _textController,
                  recorderController: recorderController,
                  onRecordingStateChange: () async {
                    final path = await recorderController.stop();
                    if (path != null && path.isNotEmpty) {}
                  },
                  textViewStateChange: () async {},
                ),
              ],
            ),
            ValueListenableBuilder<bool>(
              valueListenable: _isButtonVisible,
              builder: (context, isVisible, child) {
                return Visibility(
                  visible: isVisible,
                  child: Align(
                    alignment: Alignment.bottomCenter,
                    child: Container(
                      margin: EdgeInsets.only(bottom: 60),
                      height: 30,
                      width: 30,
                      child: FloatingActionButton(
                        onPressed: _scrollToBottom,
                        backgroundColor: Colors.grey,
                        child: const Icon(Icons.keyboard_arrow_down_rounded),
                      ),
                    ),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

MetaTouchDev avatar Mar 24 '25 03:03 MetaTouchDev

class ChatInputField extends StatefulWidget {
  final FocusNode focusNode;
  final RecorderController recorderController;
  final VoidCallback onRecordingStateChange; // Add callback to update state
  final VoidCallback textViewStateChange; // Add callback to update state
  final TextEditingController textViewController;

  const ChatInputField({
    super.key,
    required this.focusNode,
    required this.textViewController,
    required this.recorderController,
    required this.onRecordingStateChange,
    required this.textViewStateChange,
  });

  @override
  _ChatInputFieldState createState() => _ChatInputFieldState();
}

class _ChatInputFieldState extends State<ChatInputField> {
  bool isTyping = false;
  bool isRecording = false;

  @override
  void initState() {
    super.initState();
    widget.textViewController.addListener(() {
      setState(() {
        isTyping = widget.textViewController.text.trim().isNotEmpty;
      });
    });
    widget.focusNode.addListener(() {
      if (widget.focusNode.hasFocus) {
        Future.delayed(const Duration(milliseconds: 300), _scrollToBottom);
      }
    });
  }

  void _scrollToBottom() {
    if (mounted) {
      Scrollable.ensureVisible(context,
          duration: const Duration(milliseconds: 300));
    }
  }

  Future<void> _startRecording() async {
    try {
      await widget.recorderController.record();
      setState(() => isRecording = true);
    } catch (e) {
      debugPrint("Error starting recording: $e");
    }
  }

  Future<void> _stopRecording() async {
    try {
      final path = await widget.recorderController.stop();
      setState(() => isRecording = false);
      // debugPrint("Recording saved at: $path");
      // Handle sending the recorded file
    } catch (e) {
      debugPrint("Error stopping recording: $e");
    }
  }

  // This function will trigger the state update and call the parent's onPressed callback
  void voiceCallBackOnPress() async {
    setState(() {
      isRecording = !isRecording; // Toggle recording state
    });

    if (isRecording) {
      _startRecording();
    } else {
      _stopRecording();
    }
    // Call the parent callback to trigger state update
    widget.onRecordingStateChange();
  }

  void textViewCallBackOnPress() async {
    widget.textViewStateChange();
    widget.textViewController.clear();
  }

  @override
  void dispose() {
    widget.recorderController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 10, left: 5, right: 5),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(25), // Rounded corners
        child: Container(
          decoration: BoxDecoration(
            color: Colors.grey[200], // Background color
            borderRadius: BorderRadius.circular(25),
          ),
          // padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 5),
          child: Row(
            children: [
              Expanded(
                child: isRecording
                    ? ClipRRect(
                        borderRadius: BorderRadius.circular(25),
                        child: Container(
                          color: Colors.grey[100],
                          child: AudioWaveforms(
                            enableGesture: true,
                            size: Size(MediaQuery.sizeOf(context).width,
                                50), // Full width
                            recorderController: widget.recorderController,
                            waveStyle: const WaveStyle(
                              waveColor: Colors.blueAccent,
                              showMiddleLine: false,
                              extendWaveform: true,
                            ),
                          ),
                        ),
                      )
                    : AppTextField(
                        controller: widget.textViewController,
                        focusNode: widget.focusNode,
                        contentPadding: const EdgeInsets.symmetric(
                            vertical: 10, horizontal: 10),
                        hintText: 'Message',
                        boxConstraints: const BoxConstraints(
                          maxHeight: 80,
                          minHeight: 40,
                        ),
                        prefixIcon: !isTyping
                            ? Padding(
                                padding: const EdgeInsets.all(5),
                                child: Container(
                                  decoration: BoxDecoration(
                                    color: Colors.blue,
                                    borderRadius: BorderRadius.circular(25),
                                  ),
                                  child: const Icon(
                                    Icons.camera_alt,
                                    color: Colors.white,
                                  ),
                                ),
                              )
                            : null,
                      ),
              ),
              if (!isTyping && !isRecording) ...[
                IconButton(
                  onPressed: () async {
                    // Open Gallery
                  },
                  icon: const Icon(Icons.photo, color: Colors.blue),
                ),
                IconButton(
                  onPressed: _startRecording,
                  icon: const Icon(Icons.keyboard_voice_rounded,
                      color: Colors.blue),
                ),
              ] else if (isRecording) ...[
                IconButton(
                  onPressed: voiceCallBackOnPress,
                  icon: const Icon(Icons.stop, color: Colors.red),
                ),
                IconButton(
                  onPressed: _stopRecording,
                  icon: const Icon(Icons.close, color: Colors.black),
                ),
              ] else ...[
                IconButton(
                  onPressed: textViewCallBackOnPress,
                  icon: const Icon(Icons.send, color: Colors.blue),
                ),
              ],
            ],
          ),
        ),
      ),
    );
  }
}

MetaTouchDev avatar Mar 24 '25 03:03 MetaTouchDev

Dear Mr,

My code is above, but you can customize and add voice in Listview.

The issue is when I try to voice a lot and scroll up and down and it makes my app crash.

Device: Xiaomi (Redmi K50Gaming or Poco F4GT) Version: Android 13

Thank you.

MetaTouchDev avatar Mar 24 '25 03:03 MetaTouchDev