前言

在本博客中,你将了解如何在 .NET MAUI 中开发录音机和播放器。音频播放器将录制和播放音频文件。
此应用程序可以在Android和iOS上部署和使用。

预览

以下是该录音机和播放录音的应用程序屏幕截图。

先决条件

IDE: VisualStudio 2022
支持的平台:Android 和 IOS
支持的操作系统:Android(7.0 及以上)和 iOS(v12 及以上)

步骤1:在两个平台中添加所需的权限。

要录制音频并将其保存在设备中,应用程序必须访问设备的音频输入和存储。为此,需要授予以下权限:

  • RECORD_AUDIO

  • READ_EXTERNAL_STORAGE

  • WRITE_EXTERNAL_STORAGE

注意:在iOS中,您无法添加存储权限。在选中和请求时,它将始终返回"已授予"。
在 Android 中,将以下代码添加到 AndroidManifest.xml 文件中。

<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" />

在 iOS 中,将以下代码添加到 Info.plist 文件中。

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

步骤2:创建用于录制和播放音频的服务。

NET MAUI 没有能够直接使用的录制和播放音频功能。因此,必须在本机平台中创建用于录制和播放音频文件的服务。
在创建服务类之前,请创建用于调用本机方法的接口。

请参考以下代码。

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();
}

然后,创建服务以在两个平台上录制和播放音频。

安卓录音机服务

参考以下方法和属性来创建适用于 Android 的录音机服务:

  • 创建 MediaRecorder 类的实例,该实例将用于录制音频.

  • SetAudioSource(): 指定用于捕获音频输入的硬件设备.

  • SetOutputFile(): 指定输出音频文件的名称.

  • Prepare(): 初始化录音机.

  • Start(): 开始录制音频.

  • Reset(): 丢弃录制的音频并重置录音机.

  • Pause(): 将录制暂停在当前运行位置.

  • Resume(): 从暂停位置恢复录制.

  • Stop(): 停止录音.
    请参考以下代码。

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
}

适用于 iOS 的录音机服务

现在,使用AVAudioRecorder为iOS平台创建录音机服务:

  • 在尝试录制之前初始化音频会话。

  • 指定录制文件格式和保存录制内容的位置。记录格式被指定为 NSDictionary 中的条目,其中包含两个包含格式键和值的 NSObject 数组。

  • 准备好开始录制音频时调用 Record 方法。

  • 完成录制后,在录制器上调用 Stop() 方法。
    请参考以下代码。

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;
}
}

现在,来实现一个音频播放器服务,用于在两个平台上播放录制的音频。

适用于安卓的音频播放器服务

请按照以下步骤创建适用于 Android 的音频播放服务:

  • 创建 MediaPlayer 类的实例以播放音频文件。

  • 通过 SetDataSource 方法将音频文件的文件路径提供给媒体播放器实例。

  • 设置数据源后,通过调用 Prepare 方法准备媒体播放器。

  • 准备好媒体播放器后,使用 Start 方法开始播放音频。
    请参考以下代码。

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
}

适用于 iOS 的音频播放器服务

使用 AVPlayer 类在 iOS 中创建音频播放服务:

  • 将音频文件配置为通过 AVPlayerItem 内置类播放。

  • 调用 Play 方法以开始播放音频。
    请参考以下代码。

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();
}
}

步骤3:创建模型。

创建一个模型类以在列表中显示录制的音频文件。请参考以下代码。

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
}

在代码中,属性可以显示播放音频时间。

步骤4:创建 UI。

这里将创建一个简单的UI,用于显示录制的音频文件和录制音频。为此,这里使用 Syncfusion 的 ListView for .NET MAUI。安装 .NET MAUI 列表视图 NuGet 包,然后将其包含在应用程序中。

以下 XAML 代码将在 Syncfusion 的 .NET MAUI 列表视图控件中显示录制的音频。

<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=""
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=""
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>

以下 XAML 代码用于设计用于录制音频的 UI。

<!--  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=""
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=""
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=""
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=""
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=""
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>

步骤 5:注册依赖项注入以访问构造函数中的对象。

依赖关系注入是对象(客户端)接收依赖于它的其他对象(服务)的一种方式。若要了解有关在 .NET MAUI 中使用依赖项注入的详细信息.
请参阅以下代码以注册依赖项注入服务。首先,必须添加必要的服务。然后,可以直接访问所需类构造函数中的对象。因此才可以在视图模型中访问 AudioPlayerService 和 RecordAudioService 对象。

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();
}

步骤6:创建视图模型。

这里遵循 MVVM(模型-视图-视图模型)结构来开发该应用程序。
因此,需要创建一个用于录制和播放音频的视图模型(MainPageViewModel.cs)文件。
在 ViewModel 中,生成一个音频集合来绑定录制音频的数据。

ViewModel 类中的属性
recordTime,playTimer:播放或录制音频时UI中使用的计时器属性。
IsRecordingAudio: 控制重置和停止选项的可见性。
IsPauseButtonVisible:控制 UI 中“暂停”按钮的可见性。
IsRecordButtonVisible:控制“录制”按钮的可见性。
IsResumeButtonVisible:控制“恢复”按钮的可见性。
audios:要在列表视图中显示的所有录制音频文件的集合。
recordAudioService,audioPlayerService:这些接口属性调用特定于本机平台的代码。
下面的代码示例演示录音机。

使用以下代码启动录制器。必须调用依赖服务方法 recordAudio.StartRecord() 来启动记录器。

只有授予权限,才能启动记录器。

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();
}
}

使用 recordAudio.PauseRecord() 平台特定的方法暂停录制器。

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

以下方法用于从暂停位置继续录制。

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

使用以下代码重置记录器并从初始位置启动它。使用特定于平台的代码 recordAudio.ResetRecord() 重置录制器。

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

若要停止录制器,请使用特定于平台的代码 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);
}
}

以下代码用于获取录制权限

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;
}

下面的代码示例演示音频播放器。
调用特定于平台的方法 audioPlayer.PlayAudio(audioFilePath) 来播放音频。

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();
}
}

使用以下方法通过特定于平台的方法 audioPlayer.Pause() 暂停音频。

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

使用方法audioPlayer.Stop() 停止以下代码中的音频。

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

使用以下代码获取音频的当前位置并将其显示在 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();
}

.NET 中创建录音机和播放器应用的更多相关文章

  1. 【Blazor】在ASP.NET Core中使用Blazor组件 - 创建一个音乐播放器

    前言 Blazor正式版的发布已经有一段时间了,.NET社区的各路高手也创建了一个又一个的Blazor组件库,其中就包括了我和其他小伙伴一起参与的AntDesign组件库,于上周终于发布了第一个版本0 ...

  2. Asp.Net MVC中Aplayer.js音乐播放器的使用

    1.前言: Aplater.js是一款可爱.漂亮的Js音乐播放器,以前就了解过也弄过一些,现在就用mp3的格式来在.Net里面开发.管网 https://aplayer.js.org/ 2.入手: 在 ...

  3. html中嵌入flvplayer.swf播放器,播放视频

    只需要改动红色的代码: <object classid='clsid:D27CDB6E-AE6D-11cf-96B8-4445535411111' codebase='http://downlo ...

  4. 22_Android中的本地音乐播放器和网络音乐播放器的编写,本地视频播放器和网络视频播放器,照相机案例,偷拍案例实现

    1 编写以下案例: 当点击了"播放"之后,在手机上的/mnt/sdcard2/natural.mp3就会播放. 2 编写布局文件activity_main.xml <Line ...

  5. .NET 中创建支持集合初始化器的类型

    对象初始化器和集合初始化器只是语法糖,但是能让你的代码看起来更加清晰.至少能让对象初始化的代码和其他业务执行的代码分开,可读性会好一些. 本文将编写一个类型,可以使用集合初始化器构造这个类型.不只是添 ...

  6. .NET中使用APlayer组件自制播放器

    目录 说明 APlayer介绍 APlayer具备功能 APlayer使用 自制播放器Demo 未完成工作 源码下载 说明 由于需求原因,需要在项目中(桌面程序)集成一个在线播放视频的功能.大概要具备 ...

  7. 驳Linux不娱乐 堪比Win平台中十款播放器

    播放器在我们日常生活中扮演着非常重要的角色,在Windows操作系统中,播放器被应用的非常广泛,不但我们可以听音乐,甚至还可以听广播,制作铃声,下载音乐等等.而在Linux发行版中,缺少娱乐性一直性W ...

  8. 【jquery】一款不错的音频播放器——Amazing Audio Player

    前段时间分享了一款视频播放器,点击这里.今天介绍一款不错的音频播放器——Amazing Audio Player. 介绍: Amazing Audio Player 是一个使用很方便的 Windows ...

  9. Android(java)学习笔记234: 服务(service)之音乐播放器

    1.我们播放音乐,希望在后台长期运行,不希望因为内存不足等等原因,从而导致被gc回收,音乐播放终止,所以我们这里使用服务Service创建一个音乐播放器. 2.创建一个音乐播放器项目(使用服务) (1 ...

  10. Android基于发展Service音乐播放器

    这是一个基于Service组件的音乐播放器,程序的音乐将会由后台的Service组件负责播放,当后台的播放状态改变时,程序将会通过发送广播通知前台Activity更新界面:当用户单击前台Activit ...

随机推荐

  1. 几种数据库jar包获取方式

    摘要:以下提供的都是各个数据库较为官方的jar包获取方式. 本文分享自华为云社区<JDBC连接相关jar包获取及上传管理中心白名单处理>,作者:HuaWei XYe. jar包获取 以下提 ...

  2. Kubernetes监控手册-01体系概述

    Kubernetes 监控体系驳杂,涉及到的内容非常多,总是感觉摸不到头绪,网上虽然有很多资料,都略显凌乱,没有一个体系化的讲解,今天开始,我们准备撰写一系列文章,把 Kubernetes 监控说透, ...

  3. 云知声: 基于 JuiceFS 的超算平台存储实践

    云知声从一家专注于语音及语言处理的技术公司,现在技术栈已经发展到具备图像.自然语言处理.信号等全栈式的 AI 能力,是国内头部人工智能独角兽企业.公司拥抱云计算,在智慧医疗.智慧酒店.智慧教育等方面都 ...

  4. 使用python批量更改文件

    最近整理之前学爬虫存储的文件,发现有很多文件名有重复,而我有一点点强迫症,不想文件名重复,就写了一个Python代码来解决文件名重复问题 import os import random import ...

  5. gin模板语法

    输出数据: 语句:{{.}} 用法: 在html文件中调用 输出里面的结果 多个目录下定义模板: 语句:{{ define "xxx目录/xxx文件.html"}}        ...

  6. 「Goravel 上新」验证表单的三种新姿势,估计你只用过一种

    验证用户输入的数据是我们开发中最常见的需求,Goravel 提供三种验证姿势,个个简单好用! 第一种:简单直接式 根据表单内容直接校验: func (r *PostController) Store( ...

  7. Java内存区域有哪些构成?

    目录 前言 Java 内存区域 程序计数器 虚拟机栈 本地方法栈 堆 方法区 字符串常量池 运行时常量池 直接内存 小结 作者:小牛呼噜噜 | https://xiaoniuhululu.com 计算 ...

  8. gRPC入门与实操(.NET篇)

    为什么选择 gRPC 历史 长久以来,我们在前后端交互时使用WebApi + JSON方式,后端服务之间调用同样如此(或者更久远之前的WCF + XML方式).WebApi + JSON 是优选的,很 ...

  9. Java学习笔记:2022年1月9日(其二)

    Java学习笔记:2022年1月9日(其二) 摘要:这篇笔记主要记录了1月9日学习的第四章的类的基础知识,以及访问器以及访问器于多线程的意义. 目录 Java学习笔记:2022年1月9日(其二) 1. ...

  10. java入门与进阶P-2.3

    判断 if语句 一个基本的if语句由一个关键字if开头,跟上在括号里的表示条件的逻辑表达式, 然后是一对大括号"{}"之间的若干条语句.如果表示条件的逻辑表达式的结果为true,那 ...