一連の記事では、音楽プレーヤーのコア ビジネスとコードを段階的に説明します。
- [MAUI Project Combat] Music Player (1): 概要とアーキテクチャ
- [MAUI Project Combat] Music Player (2): カーネルの再生
- [MAUI Project Combat] Music Player (3): インターフェイス インタラクション
なぜこのプロジェクトを思い出したのですか?
これは古い Windows Phone 8 プロジェクトで、2014 年に趣味で「Tomato Player」というアプリを作成し、プログラミングのスキルを向上させました。
このプロジェクトのアーキテクチャは、WP8 から UWP、Xamarin.Forms へと複数回移行されています。昨年末のMAUIの正式リリースに伴い、MAUIへの移行を試みました。
いくつかの移行が行われましたが、名前空間と再生コアのコードは基本的にあまり変更されていません. ソリューションのアップグレードにより、プロジェクトはライブラリと API 呼び出しメソッドの変更に依存しており、Microsoft のモバイル インターネットでの混乱を目の当たりにしました.分野。8 年前に Microsoft Store に提出されたアプリがまだダウンロード ページ ( Microsoft App Store )を開くことができることを偶然発見しましたが、手元に Windows Phone デバイスがないため、エミュレーターで実行することはできません。 . ストアのスクリーンショットとソース コードからのみ、このオブジェクトとその時間を追体験できます。
このプロジェクトは今では商業的価値はありませんが、それが私にとって何を意味するかを知っています. プログラミングが私にもたらした喜びと楽しさは、「Code 4 Fun」が何であるかを本当に私に知らせました.当時社会人になったばかりの私が、自信をつけて道を突き進むことができて大変助かりました。
アイテムに価値がなかった可能性があります。次に、ブログ記事を書き、できるだけ多くの価値をオープンソースにします。
現在、Plugin.Maui.Audioなど、.Net プラットフォームにはオープン ソースのオーディオ パッケージ ライブラリが多数あります. このプロジェクトは、サードパーティ製のオーディオ ライブラリに依存していません. 誰もが学ぶ姿勢でコミュニケーションできることを願っています.それを実装するより良い方法があります。記事の下にメッセージを残すことを歓迎します。コードが古く、近年リファクタリングされていないため、C# 言語のバージョンとコードの記述に多くの冗長性が生じます。
建築
Abp フレームワークを使用して、以前に Abp を .NET MAUI プロジェクトに移植する、このプロジェクトもこのブログ投稿に従って完了しています。
クロスプラットフォーム
.NET MAUを使用してクロスプラットフォーム サポートを実現することで、Xamarin.Forms から移植されたアプリケーションは、Android および iOS プラットフォームでスムーズに実行できます。
The Playback Core is provided by partial classes to provide cross-platform support. Xamarin.Forms の時代には、異なるプラットフォーム上のプロジェクトを維持する必要があります. MAUI は、複数のプラットフォームをサポートする単一のプロジェクトです.
MAUI アプリケーション プロジェクトには Platform フォルダーが含まれており、各サブフォルダーは .NET MAUI がターゲットにできるプラットフォームを表します。
各フォルダーは各プラットフォームの特定のコードを表し、既定では、コンパイル フェーズは現在選択されているプラットフォーム フォルダー コードのみをコンパイルします。
これは、部分的なクラスとメソッドを使用してプラットフォーム固有のコンテンツを作成することに属します。詳細については、公式ドキュメントを参照してください。
たとえば、IMusicControlService
プロジェクトの部分クラスの実装では次のようになります。
MatoMusic.Core\Impl\MusicControlService.cs
MatoMusic.Core\Platforms\Android\MusicControlService.cs
MatoMusic.Core\Platforms\iOS\MusicControlService.cs
MatoMusic.Core\Platforms\Windows\MusicControlService.cs
コアクラス
再生カーネルを設計する際、ユーザーの対話パスからトラック マネージャーIMusicInfoManager
と再生制御サービスを抽象化しましたIMusicControlService
。
プレーヤーの動作とトラック操作の動作は、それぞれのフィールドで互いに分離されており、生産と消費のモデル、データ フロー、およびメッセージ通知のバブリングを通じて調整されます。スレッド ロックや複雑なスレッド同期ロジックを大規模に使用することは避けてください。クロスプラットフォーム ソリューションでは、これらのインターフェイスは部分クラスを通じて実装され、クラス ダイアグラムは次のようになります。
音楽再生に関連するサービス クラスは、MusicRelatedService
再生制御サービスのカプセル化の層であり、実際のプレーヤー ビジネス ロジックの観点からは、カプセル化されたコードを使用してタスクを完了する方が便利です。
このプロジェクトは MVVM デザイン パターンに従います.MusicRelatedViewModel
音楽再生に関連する ViewModel の基本クラスとして、トラック マネージャーIMusicInfoManager
と再生制御サービスIMusicControlService
オブジェクトが含まれています. 開発者は、双方向バインディングにより、プレゼンテーション レイヤーから簡単に音楽制御とトラック アクセスを実行できます.
ViewModelBase
これは基本クラスであり、AbpServiceBase
Abp フレームワークの共通関数の呼び出しを継承してカプセル化します。設定、ローカリゼーション、UnitOfWork 関数など。実装されINotifyPropertyChanged
、バインドされた型のすべてのプロパティに変更イベントを提供します。
コアクラス図は次のとおりです
意味
- キュー - 曲のキュー、現在再生中の曲の順序付きリスト
- プレイリスト - プレイリスト、再生可能なコンテンツのコレクションの保存、お気に入りのトラックの保存、お気に入りへの追加など。
- PlaylistEntry - ローカル音楽またはオンライン音楽情報に関連付けられたプレイリスト エントリ、再生可能なコンテンツ
- MyFavourite - 私のお気に入りです。ID が 1 の特別なプレイリストで、編集も削除もできません。Little Red Heart の録音とライトアップに使用されます。
- MusicInfo - トラック情報
- AlbumInfo - アルバム情報
- ArtistInfo - アーティスト情報
- BillboardInfo - チャート、オンライン音楽プレイリスト
追跡
トラックは次のとおりです。
- タイトル - 音楽のタイトル
- AlbumTitle - アルバムのタイトル
- GroupHeader - 表示に従ってリストをグループ化するために使用されるタイトル ヘッダー
- Url - 音声ファイルのアドレス
- アーティスト - アーティスト
- ジャンル - ジャンル
- IsFavourite - 「お気に入り」ですか
- IsPlaying - 再生中かどうか
- AlbumArtPath - カバー画像
- デュレーション - 曲の合計デュレーション
あいまい検索コントロールと連携する場合は実装が必要ですIClueObject
. 使用方法についてはオートコンプリートコントロールを参照してください.
public class MusicInfo : ObservableObject, IBasicInfo, IClueObject
{ .. }
public List<string> ClueStrings
{
get
{
var result = new List<string>();
result.Add(Title);
result.Add(Artist);
result.Add(AlbumTitle);
return result;
}
}
から継承しObservableObject
、コンストラクターの register プロパティ変更イベントchange で、現在のトラックを「お気に入り」として設定または設定解除するために
IsFavourite
呼び出されますMusicInfoManager
private void MusicInfo_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
var MusicInfoManager = IocManager.Instance.Resolve<MusicInfoManager>();
if (e.PropertyName == nameof(IsFavourite))
{
if (IsFavourite)
{
MusicInfoManager.CreatePlaylistEntryToMyFavourite(this);
}
else
{
MusicInfoManager.DeletePlaylistEntryFromMyFavourite(this);
}
}
}
トラックコレクション
トラック コレクションは、アーティスト (歌手) によって作成されたプレイリスト、音楽アルバム、または音楽の抽象化であり、次のものが含まれます。
- タイトル - タイトル、プレイリスト、音楽アルバムまたはアーティスト名
- GroupHeader - 表示に従ってリストをグループ化するために使用されるタイトル ヘッダー
- Musics - トラック情報のコレクション
- AlbumArtPath - カバー画像
- Count - 曲のコレクション内のトラック数
- 時間 - 曲のコレクションの合計時間
から継承しますObservableObject
AlbumInfo
、、、ArtistInfo
はすべてトラック コレクションのサブクラスですPlaylistInfo
。BillboardInfo
Musics
トラック コレクションの内容であり、型は でありObservableCollection<MusicInfo>
、キュー変更イベントは、双方向バインド時に提供されます。
コレクション内のトラック数とコレクションの合計継続時間は、この変数に依存します
public int Count => Musics.Count();
public string Time
{
get
{
var totalSec = Math.Truncate((double)Musics.Sum(c => (long)c.Duration));
var totalTime = TimeSpan.FromSeconds(totalSec);
var time = totalTime.ToString("g");
return time;
}
}
コレクションのコンテンツが追加または削除されると、曲のコレクションのトラック数と合計時間が同期して通知されます
private void _musics_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Remove || e.Action == NotifyCollectionChangedAction.Add)
{
RaisePropertyChanged(nameof(Time));
RaisePropertyChanged(nameof(Count));
}
}
GroupHeader
タイトル ヘッダーは、通常、タイトルの最初の文字を取得します。タイトルが中国語の場合は、Microsoft.International.Converters.PinYinConverter
中国語の最初の単語のピンイン イニシャルを使用します。クロスプラットフォームの実装方法は次のとおりです。
private partial string GetGroupHeader(string title)
{
string result = string.Empty;
if (!string.IsNullOrEmpty(title))
{
if (Regex.IsMatch(title.Substring(0, 1), @"^[\u4e00-\u9fa5]+$"))
{
try
{
var chinese = new ChineseChar(title.First());
result = chinese.Pinyins[0].Substring(0, 1);
}
catch (Exception ex)
{
return string.Empty;
}
}
else
{
result = title.Substring(0, 1);
}
}
return result;
}
GroupHeader
リストグループの表示に使用するコンテンツは、次の記事で詳しく説明します
データベース
Sqlite はアプリケーションでプレイリスト、曲リスト、設定などのデータの永続化として使用され
、Sqlite データベース ファイルは CodeFirst を使用して EF で初期化されます。mato.db
MatoMusic.Core プロジェクトappsettings.json
にローカル sqlite 接続文字列を追加します。
"ConnectionStrings": {
"Default": "Data Source=file:{0};"
},
...
このファイルは、コード ハードコードを構成ファイルに渡すためのプレースホルダーです。
MatoMusicCoreModule.cs で、PreInitialize をオーバーライドし、Configuration.DefaultNameOrConnectionString を設定します。
public override void PreInitialize()
{
LocalizationConfigurer.Configure(Configuration.Localization);
Configuration.Settings.Providers.Add<CommonSettingProvider>();
string documentsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), MatoMusicConsts.LocalizationSourceName);
var configuration = AppConfigurations.Get(documentsPath, development);
var connectionString = configuration.GetConnectionString(MatoMusicConsts.ConnectionStringName);
var dbName = "mato.db";
string dbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), MatoMusicConsts.LocalizationSourceName, dbName);
Configuration.DefaultNameOrConnectionString = String.Format(connectionString, dbPath);
base.PreInitialize();
}
次にエンティティクラスを定義します
再生キュー
で定義\MatoMusic.Core\Models\Entities\Queue.cs
public class Queue : FullAuditedEntity<long>
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
public long MusicInfoId { get; set; }
public int Rank { get; set; }
public string MusicTitle { get; set; }
}
曲目リスト
で定義\MatoMusic.Core\Models\Entities\Playlist.cs
public class Playlist : FullAuditedEntity<long>
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
public string Title { get; set; }
public bool IsHidden { get; set; }
public bool IsRemovable { get; set; }
public ICollection<PlaylistItem> PlaylistItems { get; set; }
}
プレイリスト エントリ
で定義\MatoMusic.Core\Models\Entities\PlaylistItem.cs
public class PlaylistItem : FullAuditedEntity<long>
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override long Id { get; set; }
public int Rank { get; set; }
public long PlaylistId { get; set; }
[ForeignKey("PlaylistId")]
public Playlist Playlist { get; set; }
public string MusicTitle { get; set; }
public long MusicInfoId { get; set; }
}
構成
データベース コンテキスト オブジェクトはMatoMusicDbContext
次のように定義されます。
public class MatoMusicDbContext : AbpDbContext
{
//Add DbSet properties for your entities...
public DbSet<Queue> Queue { get; set; }
public DbSet<Playlist> Playlist { get; set; }
public DbSet<PlaylistItem> PlaylistItem { get; set; }
...
MatoMusic.EntityFrameworkCore は、Abp.EntityFrameworkCore に依存するアプリケーション データベースの保守および管理プロジェクトです。
MatoMusic.EntityFrameworkCore プロジェクトの csproj ファイルで、次のパッケージを参照します。
<PackageReference Include="Abp.EntityFrameworkCore" Version="7.4.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Design" Version="1.1.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.0">
mato.db
プロジェクト MatoMusicEntityFrameworkCoreModule.cs では、コンテキスト オブジェクトが登録され、プログラムの初期化時に移行が実行され、ファイルがデバイス上に生成されます。
public override void PostInitialize()
{
Helper.WithDbContextHelper.WithDbContext<MatoMusicDbContext>(IocManager, RunMigrate);
if (!SkipDbSeed)
{
SeedHelper.SeedHostDb(IocManager);
}
}
public static void RunMigrate(MatoMusicDbContext dbContext)
{
dbContext.Database.Migrate();
}
プロジェクトアドレス
次の章では、プレーヤーのコア機能である再生サービス クラスを紹介します。