Главная » Разработка для Windows Phone 7 » Музыкальные классы XNA: MediaLibrary

0

Приложение, которое должно воспроизводить музыкальные файлы под управлением Windows Phone 7, использует классы пространства имен Microsoft.Xna.Framework.Media. Но сначала потребуется выполнить доступ к музыкальной библиотеке, и для этого необходим новый экземпляр MediaLibrary – этот же класс применялся для доступа к библиотеке фотографий.

Класс MediaLibrary определяет несколько свойств только для чтения, которые обеспечивают возможность доступа к музыкальной библиотеке несколькими стандартными способами. К этим свойствам относятся:

•                                 Albums (Альбомы) типа AlbumCollection (Коллекция альбомов), коллекция объектов Album.

•                                  Songs (Песни) типа SongCollection (Коллекция песен), коллекция объектов Song (Песня).

•         Artists (Исполнители) типа ArtistCollection (Коллекция исполнителей), коллекция объектов Artist (Исполнитель).

•         Genres (Жанры) типа GenreCollection (Коллекция жанров), коллекция объектов Genre (Жанр).

Каждая из этих коллекция включает всю музыку библиотеки, но организовывает ее по- разному. (Существенно упростило бы приложение наличие свойства Composer (Композитор) типа ComposerCollection (Коллекция композиторов).)

Для моих целей свойство Albums класса MediaLibrary кажется наиболее полезным. Класс AlbumCollection – это коллекция элементов типа Album, и Album имеет следующие свойства только для чтения (помимо всех остальных):

•                                  Name типа string

•                                 Artist типа Artist

•                                  Songs типа SongCollection

•                                  HasArt (Включает обложку) типа bool

Когда HasArt имеет значение true, мы можем вызывать два метода: GetAlbumArt (Получить обложку альбома) и GetThumbnail (Получить эскиз). Оба метода возвращают объекты Stream для доступа к растровому изображению обложки альбома. GetAlbumArt возвращает квадратное растровое изображение со стороной около 200 пикселов, и GetThumbnail возвращает квадратное растровое изображение со стороной около 100 пикселов.

SongCollection в экземпляре Album включает все треки альбома. (В ориентированной на композитора традиции не уместно использовать слово песня в применении к записям альбома, поскольку отдельный трек может быть частью симфонии, например, но под влиянием ориентированной на исполнителя традиции мы вынуждены мириться с таким именованием классов.) Объект Song включает несколько свойств только для чтения:

•           Name типа string

•          Album типа Album

•          Artist типа Artist

•           Duration типа TimeSpan.

Для организации музыкальной библиотеки по композитору и для целей привязки данных я ввел еще пару новых классов. Мой класс AlbumInfo (Сведения об альбоме) – это по сути класс-оболочка для XNA-класса Album:

Проект Silverlight: MusicByComposer Файл: AlbumInfo.cs

using System;

using System.Windows.Media.Imaging; using Microsoft.Xna.Framework.Media;

namespace MusicByComposer {

public class AlbumInfo : IComparable<AlbumInfo> {

BitmapImage albumArt; BitmapImage thumbnailArt;

public AlbumInfo(string shortAlbumName, Album album) {

this.ShortAlbumName = shortAlbumName; this.Album = album;

}

public string ShortAlbumName { protected set; get; } public Album Album { protected set; get; }

public BitmapSource AlbumArt {

get {

if (albumArt == null && Album.HasArt) {

BitmapImage bitmapImage = new BitmapImage(); bitmapImage.SetSource(Album.GetAlbumArt()); albumArt = bitmapImage;

}

return albumArt;

}

}

public BitmapSource ThumbnailArt {

get {

if (thumbnailArt == null && Album.HasArt) {

BitmapImage bitmapImage = new BitmapImage(); bitmapImage.SetSource(Album.GetThumbnail()); thumbnailArt = bitmapImage;

return thumbnailArt;

public int CompareTo(AlbumInfo albumInfo) {

return ShortAlbumName.CompareTo(albumInfo.ShortAlbumName);

}

}

}

В классе AlbumInfo имеется свойство типа Album и еще три новых свойства. ShortAlbumName (Краткое название альбома) – это название альбома без указания композитора или композиторов в начале (например, «Малер: Симфония No. 2» становится «Симфония No. 2»). Это свойство используется в методе CompareTo (Сравнить с) для целей сортировки. На первом из трех снимков экрана для приложения MusicByComposer можно заметить, что названия альбомов сортированы.

Методы GetAlbumArt и GetThumbnail класса Album возвращают объекты Stream. Для целей привязки я обеспечил два открытых свойства типа BitmapImage. Класс создает эти объекты лишь при первом доступе к ним и затем кэширует их для последующего использования.

Следующий класс, ComposerInfo (Сведения о композиторе), включает имя композитора и список всех объектов AlbumInfo с музыкой этого композитора:

Проект Silverlight: MusicByComposer Файл: ComposerInfo.cs

using System;

using System.Collections.Generic;

namespace MusicByComposer {

public class ComposerInfo {

public ComposerInfo(string composer, List<AlbumInfo> albums) {

Composer = composer; albums.Sort(); Albums = albums;

}

public string Composer { protected set; get; } public IList<AlbumInfo> Albums { protected set; get; }

}

}

Обратите внимание, что сортировка списка объектов AlbumInfo выполняется в конструкторе.

Класс MusicPresenter (Презентатор музыки) отвечает за доступ к музыкальной библиотеке телефона, получение всех альбомов, проверку названий альбомов на наличие имен композиторов и создание объектов типа ComposerInfo и AlbumInfo. Вся основная работа выполняется в конструкторе экземпляра: данные сохраняются в словаре, при этом имена композиторов используются как ключи, ссылающиеся на элементы типа List<AlbumInfo>:

Проект Silverlight: MusicByComposer Файл: MusicPresenter.cs

using System;

using System.Collections.Generic;

using Microsoft.Xna.Framework.Media;

namespace MusicByComposer {

public class MusicPresenter {

// Статический конструктор

static MusicPresenter() {

if (Current == null)

Current = new MusicPresenter();

}

// Конструктор экземпляра

public MusicPresenter() {

// Делаем этот класс синглтоном

if (MusicPresenter.Current != null) {

this.Composers = MusicPresenter.Current.Composers; return;

}

MediaLibrary mediaLib = new MediaLibrary(); Dictionary<string, List<AlbumInfo>> albumsByComposer =

new Dictionary<string, List<AlbumInfo>>();

foreach (Album album in mediaLib.Albums) {

int indexOfColon = album.Name.IndexOf(‘:’);

// Проверка на ошибки if (indexOfColon != -1 &&

// Двоеточие в начале названия альбома (indexOfColon == 0 ||

// Двоеточие в конце названия альбома indexOfColon == album.Name.Length – 1 || // ничего перед двоеточием

album.Name.Substring(0, indexOfColon).Trim().Length == 0 || // ничего после двоеточия

album.Name.Substring(indexOfColon + 1).Trim().Length == 0))

{

indexOfColon = -1;

}

// Основная логика обработки альбомов, включающих имя композитора

if (indexOfColon != -1) {

string[] albumComposers =

album.Name.Substring(0, indexOfColon).Split(‘,’); string shortAlbumName = album.Name.Substring(indexOfColon +

1).Trim();

bool atLeastOneEntry = false;

foreach (string composer in albumComposers) {

string trimmedComposer = composer.Trim();

if (trimmedComposer.Length > 0) {

atLeastOneEntry = true;

if (!albumsByComposer.ContainsKey(trimmedComposer)) albumsByComposer.Add(trimmedComposer,

new List<AlbumInfo>());

albumsByComposer[trimmedComposer].Add(

new AlbumInfo(shortAlbumName,

album));

// Еще один вариант ошибки: только запятые перед двоеточием

if (!atLeastOneEntry) {

indexOfColon = -1;

}

}

// Категория "Other" для альбомов без указания имени композитора

if (indexOfColon == -1) {

if (!albumsByComposer.ContainsKey("Other"))

albumsByComposer.Add("Other", new List<AlbumInfo>());

albumsByComposer["Other"].Add(new AlbumInfo(album.Name, album));

}

}

mediaLib.Dispose();

// Передаем ключи Dictionary в List для сортировки List<string> composerList = new List<string>();

foreach (string composer in albumsByComposer.Keys) composerList.Add(composer);

(composerList as List<string>).Sort();

// Определяем свойство Composers Composers = new List<ComposerInfo>();

foreach (string composer in composerList)

Composers.Add(new ComposerInfo(composer, albumsByComposer[composer]));

Current = this;

}

public static MusicPresenter Current { protected set; get; } public IList<ComposerInfo> Composers { private set; get; }

}

}

Приложению необходим только один экземпляр данного класса. Музыкальная библиотека не будет меняться в ходе выполнения приложения, поэтому нет основания для повторного выполнения этого конструктора экземпляра. По этой причине по завершении выполнения конструктор задает в качестве значения статического свойства Current (Текущий) экземпляр только что созданного MusicPresenter. Этот первый экземпляр фактически создается статическим конструктором в самом начале класса и завершается заданием свойства Composers (Композиторы) (ближе к концу описания класса), которое включает список объектов ComposerInfo. Если конструктор вызывается снова, он просто передает существующее свойство Composers в новый экземпляр.

Почему бы не сделать класс MusicPresenter статическим и не упростить весь этот процесс? Да потому что MusicPresenter используется в привязках данных в файлах XAML, и для этих привязок необходим фактический экземпляр класса. Однако доступ к этому классу должен осуществляться также из кода, для этого и пригодится свойство MusicPresenter.Current.

Этот статический конструктор выполняется, когда приложение впервые выполняет доступ к классу, конечно же, и также при повторном доступе к этому классу после захоронения. В

этом случае восстанавливать данные из MediaLibrary, безусловно, проще, чем сохранять их в изолированное хранилище.

Источник: Чарльз Петзольд, Программируем Windows Phone 7, Microsoft Press, © 2011.

По теме:

  • Комментарии