첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestSolution.zip
다운로드

[TestProject 프로젝트]

▶ MainWindow.xaml

<Window x:Class="TestProject.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:library="clr-namespace:TestLibrary;assembly=TestLibrary"
    Width="800"
    Height="600"
    Title="NAUDIO 라이브러리를 사용해 음악 재생하기">
    <Grid Background="{DynamicResource WindowBackgroundBrush}">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="100"  />
            <RowDefinition Height="*"    />
            <RowDefinition Height="100"  />
            <RowDefinition Height="40"   />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="30*" />
            <ColumnDefinition Width="60*" />
        </Grid.ColumnDefinitions>
        <Menu Grid.Row="0" Grid.ColumnSpan="2">
            <MenuItem Header="파일">
                <MenuItem Name="openFileMenuItem" 
                    Header="파일 열기" />
                <Separator />
                <MenuItem Name="closeMenuItem"
                    Header="종료" />
            </MenuItem>
            <MenuItem Header="테마">
                <MenuItem Name="defaultThemeMenuItem"
                    Header="디폴트 (테마 없음)"
                    IsCheckable="True" />
                <MenuItem Name="expressionDarkThemeMenuItem"
                    Header="다크 테마"
                    IsCheckable="True" />
                <MenuItem Name="expressionLightThemeMenuItem"
                    Header="라이트 테마"
                    IsCheckable="True" />
            </MenuItem>
        </Menu>
        <library:DigitalClock x:Name="digitalClock" Grid.Row="1" Grid.ColumnSpan="2"
            Margin="5"
            HorizontalAlignment="Center"
            ShowHours="False"
            ShowSubSeconds="True" />
        <library:AlbumArtDisplay x:Name="albumArtDisplay" Grid.Column="0" Grid.Row="2"
            Margin="5" />
        <Grid Grid.Row="2" Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <library:SpectrumAnalyzer x:Name="spectrumAnalyzer"
                Margin="5"
                BarCount="16" />
            <library:Equalizer x:Name="equalizer" Grid.Column="1"
                BandCount="7" />
        </Grid>
        <Grid Grid.Row="3" Grid.ColumnSpan="2">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"   />
                <ColumnDefinition Width="100" />
            </Grid.ColumnDefinitions>
            <library:WaveformTimeline x:Name="waveformTimeline"
                Margin="5"
                BorderThickness="1" />
            <StackPanel Grid.Column="1"
                Orientation="Vertical"
                VerticalAlignment="Center">
                <library:TimeEditor Name="repeatStartTimeEdit"
                    Margin="5"
                    IsReadOnly="True" />
                <library:TimeEditor Name="repeatStopTimeEdit"
                    Margin="5"
                    IsReadOnly="True" />
            </StackPanel>
        </Grid>
        <Grid Grid.Row="4" Grid.ColumnSpan="2"
            Margin="5">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"    />
                <ColumnDefinition Width="Auto" />
            </Grid.ColumnDefinitions>
            <TextBox Name="filePathTextBox" Grid.Column="0"
                Margin="5"
                HorizontalAlignment="Stretch"
                VerticalAlignment="Center"
                IsReadOnly="True" />
            <StackPanel Grid.Column="1"
                HorizontalAlignment="Right"
                Orientation="Horizontal">
                <Button Name="browseButton"
                    Margin="2 2 10 2"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center"
                    Height="24"
                    Width="50"
                    Content="선곡" />
                <Button Name="playButton"
                    Margin="2"
                    Width="50"
                    VerticalAlignment="Center"
                    Height="24"
                    IsEnabled="False"
                    Content="재생" />
                <Button Name="pauseButton"
                    Margin="2"
                    VerticalAlignment="Center"
                    Width="50"
                    Height="24"
                    IsEnabled="False"
                    Content="중지" />
                <Button Name="stopButton"
                    Margin="2"
                    VerticalAlignment="Center"
                    Width="50"
                    Height="24"
                    IsEnabled="False"
                    Content="중단" />
            </StackPanel>
        </Grid>
    </Grid>
</Window>

 

728x90

 

▶ MainWindow.xaml.cs

using Microsoft.Win32;
using System;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Data;

using TestLibrary;

namespace TestProject
{
    /// <summary>
    /// 메인 윈도우
    /// </summary>
    public partial class MainWindow : Window
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - MainWindow()

        /// <summary>
        /// 생성자
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            NAudioEngine naudioEngine = NAudioEngine.Instance;

            UIHelper.Bind(naudioEngine, "CanStop"       , this.stopButton         , Button.IsEnabledProperty                    );
            UIHelper.Bind(naudioEngine, "CanPlay"       , this.playButton         , Button.IsEnabledProperty                    );
            UIHelper.Bind(naudioEngine, "CanPause"      , this.pauseButton        , Button.IsEnabledProperty                    );
            UIHelper.Bind(naudioEngine, "SelectionBegin", this.repeatStartTimeEdit, TimeEditor.ValueProperty, BindingMode.TwoWay);
            UIHelper.Bind(naudioEngine, "SelectionEnd"  , this.repeatStopTimeEdit , TimeEditor.ValueProperty, BindingMode.TwoWay);     

            this.spectrumAnalyzer.RegisterSpectrumPlayer(naudioEngine);

            this.waveformTimeline.RegisterSoundPlayer(naudioEngine);

            LoadExpressionDarkTheme();

            naudioEngine.PropertyChanged              += naudioEngine_PropertyChanged;
            this.openFileMenuItem.Click               += openFileMenuItem_Click;
            this.closeMenuItem.Click                  += closeMenuItem_Click;
            this.defaultThemeMenuItem.Checked         += defaultThemeMenuItem_Checked;
            this.expressionDarkThemeMenuItem.Checked  += expressionDarkThemeMenuItem_Checked;
            this.expressionLightThemeMenuItem.Checked += expressionLightThemeMenuItem_Checked;
            this.browseButton.Click                   += browseButton_Click;
            this.playButton.Click                     += playButton_Click;
            this.pauseButton.Click                    += pauseButton_Click;
            this.stopButton.Click                     += stopButton_Click;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Protected

        #region 닫을 경우 처리하기 - OnClosing(e)

        /// <summary>
        /// 닫을 경우 처리하기
        /// </summary>
        /// <param name="e">이벤트 인자</param>
        protected override void OnClosing(CancelEventArgs e)
        {
            NAudioEngine.Instance.Dispose();
        }

        #endregion

        ////////////////////////////////////////////////////////////////////////////////////////// Private
        //////////////////////////////////////////////////////////////////////////////// Event

        #region NAUDIO 엔진 속성 변경시 처리하기 - naudioEngine_PropertyChanged(sender, e)

        /// <summary>
        /// NAUDIO 엔진 속성 변경시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void naudioEngine_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            NAudioEngine naudioEngine = NAudioEngine.Instance;

            switch(e.PropertyName)
            {
                case "FileTag" :

                    if(naudioEngine.FileTag != null)
                    {
                        TagLib.Tag tag = naudioEngine.FileTag.Tag;

                        if(tag.Pictures.Length > 0)
                        {
                            using(MemoryStream memoryStream = new MemoryStream(tag.Pictures[0].Data.Data))
                            {
                                try
                                {
                                    BitmapImage bitmapImage = new BitmapImage();

                                    bitmapImage.BeginInit();

                                    bitmapImage.CacheOption  = BitmapCacheOption.OnLoad;
                                    bitmapImage.StreamSource = memoryStream;

                                    bitmapImage.EndInit();

                                    this.albumArtDisplay.AlbumArtImage = bitmapImage;
                                }
                                catch(NotSupportedException)
                                {
                                    this.albumArtDisplay.AlbumArtImage = null;
                                }

                                memoryStream.Close();
                            }
                        }
                        else
                        {
                            this.albumArtDisplay.AlbumArtImage = null;
                        }
                    }
                    else
                    {
                        this.albumArtDisplay.AlbumArtImage = null;
                    }

                    break;

                case "ChannelPosition" :

                    this.digitalClock.Time = TimeSpan.FromSeconds(naudioEngine.ChannelPosition);

                    break;   

                default:

                    break;
            }
        }

        #endregion

        #region 파일 열기 메뉴 항목 클릭시 처리하기 - openFileMenuItem_Click(sender, e)

        /// <summary>
        /// 파일 열기 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void openFileMenuItem_Click(object sender, RoutedEventArgs e)
        {
            OpenFile();
        }

        #endregion
        #region 닫기 메뉴 항목 클릭시 처리하기 - closeMenuItem_Click(sender, e)

        /// <summary>
        /// 닫기 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void closeMenuItem_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        #endregion
        #region 디폴트 테마 메뉴 항목 클릭시 처리하기 - defaultThemeMenuItem_Checked(sender, e)

        /// <summary>
        /// 디폴트 테마 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void defaultThemeMenuItem_Checked(object sender, RoutedEventArgs e)
        {
            LoadDefaultTheme();
        }

        #endregion
        #region 익스프레션 다크 테마 메뉴 항목 클릭시 처리하기 - expressionDarkThemeMenuItem_Checked(sender, e)

        /// <summary>
        /// 익스프레션 다크 테마 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void expressionDarkThemeMenuItem_Checked(object sender, RoutedEventArgs e)
        {
            LoadExpressionDarkTheme();
        }

        #endregion
        #region 익스프레션 라이트 테마 메뉴 항목 클릭시 처리하기 - expressionLightThemeMenuItem_Checked(sender, e)

        /// <summary>
        /// 익스프레션 라이트 테마 메뉴 항목 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void expressionLightThemeMenuItem_Checked(object sender, RoutedEventArgs e)
        {
            LoadExpressionLightTheme();
        }

        #endregion

        #region 선곡 버튼 클릭시 처리하기 - browseButton_Click(sender, e)

        /// <summary>
        /// 선곡 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void browseButton_Click(object sender, RoutedEventArgs e)
        {
            OpenFile();
        }

        #endregion
        #region 재생 버튼 클릭시 처리하기 - playButton_Click(sender, e)

        /// <summary>
        /// 재생 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void playButton_Click(object sender, RoutedEventArgs e)
        {
            if(NAudioEngine.Instance.CanPlay)
            {
                NAudioEngine.Instance.Play();
            }
        }

        #endregion
        #region 중지 버튼 클릭시 처리하기 - pauseButton_Click(sender, e)

        /// <summary>
        /// 중지 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void pauseButton_Click(object sender, RoutedEventArgs e)
        {
            if(NAudioEngine.Instance.CanPause)
            {
                NAudioEngine.Instance.Pause();
            }
        }

        #endregion
        #region 중단 버튼 클릭시 처리하기 - stopButton_Click(sender, e)

        /// <summary>
        /// 중단 버튼 클릭시 처리하기
        /// </summary>
        /// <param name="sender">이벤트 발생자</param>
        /// <param name="e">이벤트 인자</param>
        private void stopButton_Click(object sender, RoutedEventArgs e)
        {
            if(NAudioEngine.Instance.CanStop)
            {
                NAudioEngine.Instance.Stop();
            }
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////// Event

        #region 파일 열기 - OpenFile()

        /// <summary>
        /// 파일 열기
        /// </summary>
        private void OpenFile()
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.Filter = "MP3 파일|*.mp3";

            if(openFileDialog.ShowDialog() == true)
            {
                NAudioEngine.Instance.OpenFile(openFileDialog.FileName);

                this.filePathTextBox.Text = openFileDialog.FileName;
            }
        }

        #endregion
        #region 디폴트 테마 로드하기 - LoadDefaultTheme()

        /// <summary>
        /// 디폴트 테마 로드하기
        /// </summary>
        private void LoadDefaultTheme()
        {
            this.defaultThemeMenuItem.IsChecked         = true;
            this.defaultThemeMenuItem.IsEnabled         = false;
            this.expressionDarkThemeMenuItem.IsChecked  = false;
            this.expressionDarkThemeMenuItem.IsEnabled  = true;
            this.expressionLightThemeMenuItem.IsChecked = false;
            this.expressionLightThemeMenuItem.IsEnabled = true;

            Resources.MergedDictionaries.Clear();
        }

        #endregion
        #region 익스프레션 다크 테마 로드하기 - LoadExpressionDarkTheme()

        /// <summary>
        /// 익스프레션 다크 테마 로드하기
        /// </summary>
        private void LoadExpressionDarkTheme()
        {
            this.defaultThemeMenuItem.IsChecked         = false;
            this.defaultThemeMenuItem.IsEnabled         = true;
            this.expressionDarkThemeMenuItem.IsChecked  = true;
            this.expressionDarkThemeMenuItem.IsEnabled  = false;
            this.expressionLightThemeMenuItem.IsChecked = false;
            this.expressionLightThemeMenuItem.IsEnabled = true;

            Resources.MergedDictionaries.Clear();

            ResourceDictionary resourceDictionary = Application.LoadComponent(new Uri("ExpressionDark.xaml", UriKind.Relative)) as ResourceDictionary;

            Resources.MergedDictionaries.Add(resourceDictionary);
        }

        #endregion
        #region 익스프레션 라이트 테마 로드하기 - LoadExpressionLightTheme()

        /// <summary>
        /// 익스프레션 라이트 테마 로드하기
        /// </summary>
        private void LoadExpressionLightTheme()
        {
            this.defaultThemeMenuItem.IsChecked         = false;
            this.defaultThemeMenuItem.IsEnabled         = true;
            this.expressionDarkThemeMenuItem.IsChecked  = false;
            this.expressionDarkThemeMenuItem.IsEnabled  = true;
            this.expressionLightThemeMenuItem.IsChecked = true;
            this.expressionLightThemeMenuItem.IsEnabled = false;

            Resources.MergedDictionaries.Clear();

            ResourceDictionary themeResources = Application.LoadComponent(new Uri("ExpressionLight.xaml", UriKind.Relative)) as ResourceDictionary;

            Resources.MergedDictionaries.Add(themeResources);
        }

        #endregion
    }
}

※ 컴파일시 TestProject 프로젝트의 플랫폼 대상을 x86으로 설정해야 한다.

728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요