Loading

Create recorder and player application in. NET MAUI (transfer)

preface

In this blog, you will learn how to use the NET MAUI. The audio player records and plays back audio files.
This application can be deployed and used on Android and iOS.

preview

The following is a screenshot of the recorder and the application that plays the recording.

precondition

IDE: VisualStudio 2022
Supported platforms: Android and IOS
Supported operating systems: Android (7.0 and above) and iOS (v12 and above)

Step 1: Add the required permissions in the two platforms.

To record audio and save it on the device, the application must access the audio input and storage of the device. To do this, you need to grant the following permissions:

  • RECORD_AUDIO
  • READ_EXTERNAL_STORAGE
  • WRITE_EXTERNAL_STORAGE

Note: You cannot add storage permissions in iOS. When selected and requested, it will always return Granted.
In Android, add the following code to the AndroidManifest.xml file.

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />

In iOS, add the following code to the Info.plist file.

 <key>NSMicrophoneUsageDescription</key> <string>The audio recorder app wants to use your microphone to record audio.</ string>

Step 2: Create a service for recording and playing audio.

NET MAUI does not have recording and playing audio functions that can be used directly. Therefore, services for recording and playing audio files must be created in the native platform.
Before creating a service class, create an interface for calling native methods.

Refer to the following code.

 public interface IAudioPlayer { void PlayAudio(string filePath); void Pause(); void Stop(); string GetCurrentPlayTime(); bool CheckFinishedPlayingAudio(); } public interface IRecordAudio { void StartRecord(); string StopRecord(); void PauseRecord(); void ResetRecord(); }

Then, create services to record and play audio on both platforms.

Android recorder service

Refer to the following methods and properties to create a recorder service for Android:

  • Create an instance of the MediaRecorder class, which will be used to record audio
  • SetAudioSource(): Specifies the hardware device used to capture audio input
  • SetOutputFile(): Specifies the name of the output audio file
  • Prepare(): initialize the recorder
  • Start(): Start recording audio
  • Reset(): Discard the recorded audio and reset the recorder
  • Pause(): Pause recording at the current running position
  • Resume(): Resume recording from the paused position
  • Stop(): Stop recording
    Refer to the following code.
 public class RecordAudio : IRecordAudio { #region Fields private MediaRecorder mediaRecorder; private string storagePath; private bool isRecordStarted = false; #endregion #region Methods public void StartRecord() { if (mediaRecorder == null) { SetAudioFilePath(); mediaRecorder = new MediaRecorder(); mediaRecorder.Reset(); mediaRecorder.SetAudioSource(AudioSource. Mic); mediaRecorder.SetOutputFormat(OutputFormat. AacAdts); mediaRecorder.SetAudioEncoder(AudioEncoder. Aac); mediaRecorder.SetOutputFile(storagePath); mediaRecorder.Prepare(); mediaRecorder.Start(); } else { mediaRecorder.Resume(); } isRecordStarted = true; } public void PauseRecord() { if (mediaRecorder == null) { return; } mediaRecorder.Pause(); isRecordStarted = false; } public void ResetRecord() { if (mediaRecorder !=  null) { mediaRecorder.Resume(); mediaRecorder.Reset(); } mediaRecorder = null; isRecordStarted = false; } public string StopRecord() { if (mediaRecorder == null) { return string.Empty; } mediaRecorder.Resume(); mediaRecorder.Stop(); mediaRecorder = null; isRecordStarted = false; return storagePath; } private void SetAudioFilePath() { string fileName = "/Record_" + DateTime.UtcNow.ToString("ddMMM_hhmmss") + ".mp3"; var path = Environment.GetFolderPath(System. Environment.SpecialFolder.MyDocuments); storagePath = path + fileName; Directory.CreateDirectory(path); } #endregion }

Recorder service for iOS

Now, use AVAudioRecorder to create recorder service for iOS platform:

  • Initialize the audio session before attempting to record.
  • Specify the recording file format and where to save the recording. The record format is specified as an entry in the NSDictionary, which contains two NSObject arrays containing format keys and values.
  • Call the Record method when you are ready to start recording audio.
  • After recording, call the Stop() method on the recorder.
    Refer to the following code.
 public class RecordAudio : IRecordAudio { AVAudioRecorder recorder; NSUrl url; NSError error; NSDictionary settings; string audioFilePath; public RecordAudio() { InitializeAudioSession(); } private bool InitializeAudioSession() { var audioSession = AVAudioSession.SharedInstance(); var err = audioSession.SetCategory(AVAudioSessionCategory. PlayAndRecord); if (err !=  null) { Console.WriteLine("audioSession: {0}", err); return false; } err = audioSession.SetActive(true); if (err !=  null) { Console.WriteLine("audioSession: {0}", err); return false; } return false; } public void PauseRecord() { recorder.Pause(); } public void ResetRecord() { recorder.Dispose(); recorder = null; } public void StartRecord() { if (recorder == null) { string fileName = "/Record_" + DateTime.UtcNow.ToString("ddMMM_hhmmss") + ".wav"; var docuFolder = Environment.GetFolderPath(Environment. SpecialFolder.MyDocuments); audioFilePath = docuFolder + fileName; url = NSUrl.FromFilename(audioFilePath); NSObject[] values = new NSObject[] { NSNumber.FromFloat(44100.0f),  NSNumber.FromInt32((int)AudioToolbox. AudioFormatType.LinearPCM),  NSNumber.FromInt32(2),  NSNumber.FromInt32(16),  NSNumber.FromBoolean(false), NSNumber.FromBoolean(false)  }; NSObject[] key = new NSObject[] { AVAudioSettings.AVSampleRateKey, AVAudioSettings.AVFormatIDKey, AVAudioSettings.AVNumberOfChannelsKey, AVAudioSettings.AVLinearPCMBitDepthKey, AVAudioSettings.AVLinearPCMIsBigEndianKey, AVAudioSettings.AVLinearPCMIsFloatKey }; settings = NSDictionary.FromObjectsAndKeys(values, key); recorder = AVAudioRecorder.Create(url, new AudioSettings(settings), out error); recorder.PrepareToRecord(); recorder.Record(); } else { recorder.Record(); } } public string StopRecord() { if (recorder == null) { return string.Empty; } recorder.Stop(); recorder = null; return audioFilePath; } }

Now, let's implement an audio player service to play recorded audio on both platforms.

Audio player service for Android

Please follow the steps below to create an audio playback service for Android:

  • Create an instance of the MediaPlayer class to play the audio file.
  • The file path of the audio file is provided to the media player instance through the SetDataSource method.
  • After setting the data source, prepare the media player by calling the Prepare method.
  • After the media player is ready, use the Start method to start playing audio.
    Refer to the following code.
 public class AudioPlayer  : IAudioPlayer { #region Fields private MediaPlayer _mediaPlayer; private int currentPositionLength = 0; private bool isPrepared; private bool isCompleted; #endregion #region Methods public void PlayAudio(string filePath) { if (_mediaPlayer !=  null && !_mediaPlayer.IsPlaying) { _mediaPlayer. SeekTo(currentPositionLength); currentPositionLength = 0; _mediaPlayer. Start(); } else if (_mediaPlayer == null || !_ mediaPlayer.IsPlaying) { try { isCompleted = false; _mediaPlayer = new MediaPlayer(); _mediaPlayer. SetDataSource(filePath); _mediaPlayer. SetAudioStreamType(Stream. Music); _mediaPlayer. PrepareAsync(); _mediaPlayer. Prepared += (sender, args) => { isPrepared = true; _mediaPlayer. Start(); }; _mediaPlayer. Completion += (sender, args) => { isCompleted = true; }; } catch (Exception e) { _mediaPlayer = null; } } } public void Pause() { if (_mediaPlayer !=  null && _mediaPlayer.IsPlaying) { _mediaPlayer. Pause(); currentPositionLength = _mediaPlayer.CurrentPosition; } } public void Stop() { if (_mediaPlayer !=  null) { if (isPrepared) { _mediaPlayer. Stop(); _mediaPlayer. Release(); isPrepared = false; } isCompleted = false; _mediaPlayer = null; } } public string GetCurrentPlayTime() { if (_mediaPlayer !=  null) { var positionTimeSeconds =  double.Parse(_mediaPlayer. CurrentPosition.ToString()); positionTimeSeconds = positionTimeSeconds / 1000; TimeSpan currentTime = TimeSpan.FromSeconds(positionTimeSeconds); string currentPlayTime = string.Format("{0:mm\\:ss}", currentTime); return currentPlayTime; } return null; } public bool CheckFinishedPlayingAudio() { return isCompleted; } #endregion }

Audio player service for iOS

Use the AVPlayer class to create an audio playback service in iOS:

  • Configure the audio file to play through the AVPlayerItem built-in class.
  • Call the Play method to start playing audio.
    Refer to the following code.
 public class AudioPlayer : IAudioPlayer { AVPlayer _player; NSObject notificationHandle; NSUrl url; private bool isFinishedPlaying; private bool isPlaying; public bool IsPlaying { get { return isPlaying; } set { if (_player. Rate == 1 && _player.Error == null) isPlaying = true; else isPlaying = false; } } public AudioPlayer() { RegisterNotification(); } ~AudioPlayer() { UnregisterNotification(); } public void PlayAudio(string filePath) { isFinishedPlaying = false; if (_player == null) { url = NSUrl.FromString(filePath); AVPlayerItem avPlayerItem = new AVPlayerItem(URL); _player = new AVPlayer(avPlayerItem); _player. AutomaticallyWaitsToMinimizeStalling = false; _player. Volume = 1; _player. Play(); IsPlaying = true; isFinishedPlaying = false; } else if (_player !=  null && !IsPlaying) { _player. Play(); IsPlaying = true; isFinishedPlaying = false; } } public void Pause() { if (_player !=  null && IsPlaying) { _player. Pause(); IsPlaying = false; } } public void Stop() { if (_player !=  null) { _player. Dispose(); IsPlaying = false; _player = null; } } public string GetCurrentPlayTime() { if (_player !=  null) { var positionTimeSeconds = _player.CurrentTime.Seconds; TimeSpan currentTime = TimeSpan.FromSeconds(positionTimeSeconds); string currentPlayTime = string.Format("{0:mm\\:ss}", currentTime); return currentPlayTime; } return null; } public bool CheckFinishedPlayingAudio() { return isFinishedPlaying; } private void RegisterNotification() { notificationHandle = NSNotificationCenter.DefaultCenter.AddObserver(AVPlayerItem. DidPlayToEndTimeNotification, HandleNotification); } private void UnregisterNotification() { NSNotificationCenter.DefaultCenter.RemoveObserver(notificationHandle); } private void HandleNotification(NSNotification notification) { isFinishedPlaying = true; Stop(); } }

Step 3: Create the model.

Create a model class to display the recorded audio files in the list. Refer to the following code.

 public class Audio : INotifyPropertyChanged { #region Private private bool isPlayVisible; private bool isPauseVisible; private string currentAudioPostion; #endregion #region Constructor public Audio() { IsPlayVisible = true; } #endregion #region Properties public string AudioName { get;  set; } public string AudioURL { get;  set; } public string Caption { get;  set; } public bool IsPlayVisible { get { return isPlayVisible; } set { isPlayVisible = value; OnPropertyChanged(); IsPauseVisble = !value; } } public bool IsPauseVisble { get { return isPauseVisible; } set { isPauseVisible = value;  OnPropertyChanged(); } } public string CurrentAudioPosition { get { return currentAudioPostion; } set { if (string.IsNullOrEmpty(currentAudioPostion)) { currentAudioPostion = string.Format("{0:mm\\:ss}", new TimeSpan()); } else { currentAudioPostion = value; } OnPropertyChanged(); } } #endregion }

In the code, the attribute can display the playing time of the audio.

Step 4: Create the UI.

A simple UI will be created here to display recorded audio files and recorded audio. To do this, we use Syncfusion's ListView for NET MAUI。 Installation NET MAUI list view NuGet package, and then include it in the application.

The following XAML code will be displayed in Syncfusion's NET MAUI list view control displays the recorded audio.

 <syncfusion:SfListView x:Name="AudioList" Grid.Row="0" Grid.ColumnSpan="2" Margin="0,8" IsVisible="true" ItemsSource="{Binding Audios}" SelectionMode="None"> <syncfusion:SfListView. ItemTemplate> <DataTemplate> <ViewCell> <Grid x:Name="PlayAudioGrid" Margin="0,4,0,12" BackgroundColor="Transparent" HeightRequest="60"> <Grid. ColumnDefinitions> <ColumnDefinition Width="50" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="50" /> <ColumnDefinition Width="80" /> </Grid.ColumnDefinitions> <Button Grid.Column="0" Padding="0" BackgroundColor="Transparent" Command="{Binding Path=BindingContext.PlayAudioCommand, Source={x:Reference mainPage}}" CommandParameter="{Binding .}" FontFamily="AudioIconFonts" FontSize="22" IsVisible="{Binding IsPlayVisible}" Text="&#xea15; " TextColor="Black" /> <Button Grid.Column="0" Padding="0" BackgroundColor="Transparent" BorderColor="LightGray" Command="{Binding Path=BindingContext.PauseAudioCommand, Source={x:Reference mainPage}}" CommandParameter="{Binding .}" FontFamily="AudioIconFonts" FontSize="22" IsVisible="{Binding IsPauseVisble}" Text="&#xea16; " TextColor="Black" /> <Label Grid.Column="1" FontSize="14" Text="{Binding AudioName}" TextColor="Black" VerticalTextAlignment="Center" /> <Label Grid.Column="2" Margin="0,0,12,0" FontSize="14" IsVisible="{Binding IsPauseVisble}" Text="{Binding CurrentAudioPosition}" TextColor="Black" VerticalTextAlignment="Center" /> <Button Grid.Column="3" BackgroundColor="Transparent" Command="{Binding Path=BindingContext.DeleteCommand, Source={x:Reference mainPage}}" CommandParameter="{Binding}" FontFamily="AudioIconFonts" FontSize="20" Text="&#xe9ac" TextColor="Red" /> </Grid> </ViewCell> </DataTemplate> </syncfusion:SfListView. ItemTemplate> </syncfusion:SfListView>

The following XAML code is used to design the UI for recording audio.

 <!--   Timer Label  --> <StackLayout Grid.Row="2" Grid.ColumnSpan="2" Margin="0,0,0,32" VerticalOptions="End"> <Label FontSize="14" HorizontalTextAlignment="Center" IsVisible="{Binding IsRecordingAudio}" Text="Recording…" TextColor="#7D898F" /> <Label FontSize="60" HorizontalTextAlignment="Center" IsVisible="{Binding IsRecordingAudio}" Text="{Binding TimerLabel}" TextColor="Black" /> </StackLayout> <!--   Button Setup  --> <Grid Grid.Row="3" Grid.ColumnSpan="2" ColumnSpacing="60"> <Grid. ColumnDefinitions> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="*" /> </Grid.ColumnDefinitions> <!--   Retry  --> <Grid Grid.Column="0" RowDefinitions="auto,auto"> <Button Grid.Row="0" BackgroundColor="LightGray" BorderColor="#5F49FF" BorderWidth="1" Command="{Binding ResetCommand}" CornerRadius="25" FontFamily="AudioIconFonts" FontSize="22" HeightRequest="50" IsEnabled="{Binding IsRecordingAudio}" Text="&#xe900; " TextColor="#5F49FF" WidthRequest="50"> <Button. Triggers> <DataTrigger Binding="{Binding IsRecordingAudio}" TargetType="Button" Value="False"> <Setter Property="TextColor" Value="Gray" /> <Setter Property="BorderColor" Value="Gray" /> </DataTrigger> </Button. Triggers> </Button> <Label Grid.Row="1" HorizontalOptions="Center" Text="Retry" /> </Grid> <!--   Play  --> <Grid Grid.Column="1" HorizontalOptions="CenterAndExpand" RowDefinitions="auto,auto"> <!--   Record Button  --> <Button Grid.Row="0" BackgroundColor="Red" BorderColor="Red" BorderWidth="1" Command="{Binding RecordCommand}" CornerRadius="25" FontFamily="AudioIconFonts" FontSize="22" HeightRequest="50" IsVisible="{Binding IsRecordButtonVisible}" Text="&#xe91e; " TextColor="White" WidthRequest="50" /> <Label Grid.Row="1" HorizontalOptions="Center" IsVisible="{Binding IsRecordButtonVisible}" Text="Record" /> <!--   Pause Button  --> <Button Grid.Row="0" BackgroundColor="Green" BorderColor="Green" BorderWidth="1" Command="{Binding PauseCommand}" CornerRadius="25" FontFamily="AudioIconFonts" FontSize="22" HeightRequest="50" IsVisible="{Binding IsPauseButtonVisible}" Text="&#xea1d; " TextColor="White" WidthRequest="50" /> <Label Grid.Row="1" HorizontalOptions="Center" IsVisible="{Binding IsPauseButtonVisible}" Text="Pause" /> <!--   Resume Button  --> <Button Grid.Row="0" BackgroundColor="Red" BorderColor="Red" BorderWidth="1" Command="{Binding RecordCommand}" CornerRadius="25" FontFamily="AudioIconFonts" FontSize="22" HeightRequest="50" IsVisible="{Binding IsResumeButtonVisible}" Text="&#xea1c; " TextColor="White" WidthRequest="50" /> <Label Grid.Row="1" HorizontalOptions="Center" IsVisible="{Binding IsResumeButtonVisible}" Text="Resume" /> </Grid> <!--   Stop  --> <Grid Grid.Column="2" RowDefinitions="auto,auto"> <Button Grid.Row="0" BackgroundColor="LightGray" BorderColor="#5F49FF" BorderWidth="1" Command="{Binding StopCommand}" CornerRadius="25" FontFamily="AudioIconFonts" FontSize="22" HeightRequest="50" IsEnabled="{Binding IsRecordingAudio}" Text="&#xea1e; " TextColor="#5F49FF" WidthRequest="50"> <Button. Triggers> <DataTrigger Binding="{Binding IsRecordingAudio}" TargetType="Button" Value="False"> <Setter Property="TextColor" Value="Gray" /> <Setter Property="BorderColor" Value="Gray" /> </DataTrigger> </Button. Triggers> </Button> <Label Grid.Row="1" HorizontalOptions="Center" Text="Stop" /> </Grid> </Grid>

Step 5: Register dependency injection to access the object in the constructor.

Dependency injection is a way for an object (client) to receive other objects (services) that depend on it. To learn more about NET MAUI
See the following code to register the dependency injection service. First, you must add the necessary services. Then, you can directly access the objects in the required class constructor. Therefore, the AudioPlayerService and RecordAudioService objects can be accessed in the view model.

 public static MauiApp CreateMauiApp() { var builder = MauiApp.CreateBuilder(); builder .UseMauiApp<App>() .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold"); fonts.AddFont("AudioIconFonts.ttf", "AudioIconFonts"); }); #if ANDROID || IOS builder.Services.AddTransient<IAudioPlayerService, AudioPlayerService>(); builder.Services.AddTransient<IRecordAudioService, RecordAudioService>(); #endif builder.Services.AddTransient<MainPage>(); builder.Services.AddTransient<AppShell>(); builder.ConfigureSyncfusionListView(); return builder.Build(); }

Step 6: Create the view model.

This application is developed following the MVVM (model view view model) structure.
Therefore, you need to create a view model (MainPageViewModel. cs) file for recording and playing audio.
In ViewModel, generate an audio collection to bind the recorded audio data.

Properties in the ViewModel class
RecordTime, playTimer: the timer attribute used in the UI when playing or recording audio.
IsRecordingAudio: Controls the visibility of reset and stop options.
IsPauseButtonVisible: Controls the visibility of the Pause button in the UI.
IsRecordButtonVisible: Controls the visibility of the Record button.
IsResumeButtonVisible: Controls the visibility of the Restore button.
Audios: the collection of all recorded audio files to be displayed in the list view.
RecordAudioService, audioPlayerService: These interface properties call the code specific to the native platform.
The following code example demonstrates a recorder.

Use the following code to start the recorder. You must call the dependent service method recordAudio StartRecord() to start the recorder.

You must grant permission to start the logger.

 private async void StartRecording() { if (! IsRecordingAudio) { var permissionStatus = await RequestandCheckPermission(); if (permissionStatus == PermissionStatus. Granted) { IsRecordingAudio = true; IsPauseButtonVisible = true; recordAudio.StartRecord(); IsRecordButtonVisible = false; isRecord = true; timerValue = new TimeSpan(0, 0, -1); recordTimer.Start(); } else { IsRecordingAudio = false; IsPauseButtonVisible = false; } } else { ResumeRecording(); } }

Use recordAudio PauseRecord() platform specific methods pause the recorder.

 private void PauseRecording() { isRecord = false; IsPauseButtonVisible = false; IsResumeButtonVisible = true; recordAudio.PauseRecord(); }

The following method is used to resume recording from the paused position.

 private void ResumeRecording() { recordAudio.StartRecord(); IsResumeButtonVisible = false; IsPauseButtonVisible = true; isRecord = true; }

Use the following code to reset the recorder and start it from its original location. Use platform specific code recordAudio ResetRecord() resets the recorder.

 private void ResetRecording() { recordAudio.ResetRecord(); timerValue = new TimeSpan(); TimerLabel = string.Format("{0:mm\\:ss}", timerValue); IsRecordingAudio = false; IsPauseButtonVisible = false; IsResumeButtonVisible = false; StartRecording(); }

To stop the recorder, use the platform specific code recordAudio StopRecord()。

 private async void StopRecording() { IsPauseButtonVisible = false; IsResumeButtonVisible = false; IsRecordingAudio = false; IsRecordButtonVisible = true; timerValue = new TimeSpan(); recordTimer.Stop(); RecentAudioFilePath = recordAudio.StopRecord(); await App.Current.MainPage.DisplayAlert("Alert", "Audio has been recorded", "Ok"); TimerLabel = string.Format("{0:mm\\:ss}", timerValue); SendRecording(); } private void SendRecording() { Audio recordedFile = new Audio() { AudioURL = RecentAudioFilePath }; if (recordedFile !=  null) { recordedFile.AudioName = Path.GetFileName(RecentAudioFilePath); Audios.Insert(0, recordedFile); } }

The following code is used to obtain recording permission

 public async Task<PermissionStatus> RequestandCheckPermission() { PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.StorageWrite>(); if (status !=  PermissionStatus.Granted) await Permissions.RequestAsync<Permissions.StorageWrite>(); status = await Permissions.CheckStatusAsync<Permissions.Microphone>(); if (status !=  PermissionStatus.Granted) await Permissions.RequestAsync<Permissions.Microphone>(); PermissionStatus storagePermission = await Permissions.CheckStatusAsync<Permissions.StorageWrite>(); PermissionStatus microPhonePermission = await Permissions.CheckStatusAsync<Permissions.Microphone>(); if (storagePermission == PermissionStatus. Granted && microPhonePermission == PermissionStatus.Granted)  { return PermissionStatus.Granted; } return PermissionStatus.Denied; }

The following code example demonstrates the audio player.
Call the platform specific method audioPlayer PlayAudio (audioFilePath) to play audio.

 private void StartPlayingAudio(object obj) { if (audioFile !=  null && audioFile != (Audio)obj) { AudioFile.IsPlayVisible = true; StopAudio(); } if (obj is Audio) { audioFile = (Audio)obj; audioFile.IsPlayVisible = false; string audioFilePath = AudioFile.AudioURL; audioPlayer.PlayAudio(audioFilePath); SetCurrentAudioPosition(); } }

Use the following method to pass the platform specific method audioPlayer Pause() pauses the audio.

 private void PauseAudio(object obj) { if (obj is Audio) { var audiophile = (Audio)obj; audioFile.IsPlayVisible = true; audioPlayer.Pause(); } }

Use method audioPlayer Stop() stops the audio in the following code.

 public void StopAudio() { if (AudioFile !=  null) { audioPlayer.Stop(); playTimer.Stop(); } }

Use the following code to get the current location of the audio and display it in the UI.

 private void SetCurrentAudioPosition() { playTimer.Interval = new TimeSpan(0, 0, 0, 0, 250); playTimer.Tick += (s, e) => { if (AudioFile !=  null) { AudioFile.CurrentAudioPosition = audioPlayer.GetCurrentPlayTime(); bool isAudioCompleted = audioPlayer.CheckFinishedPlayingAudio(); if (isAudioCompleted) { AudioFile.IsPlayVisible = true; playTimer.Stop(); } } }; playTimer.Start(); }

reference resources

1.Android:
Android Audio
2.iOS:
AVAudioPlayer Class
AVAudioRecorder Class

resources

For more details, see GitHub project .NET MAUI Audio Recorder and Player

posted @ 2023-01-15 22:00   Trace g   Reading( one thousand six hundred and fifty-two Comments( two edit   Collection   report