├── AudioRecorder
├── Assets
│ ├── Play.png
│ ├── Save.png
│ ├── Stop.png
│ ├── Delete.png
│ ├── Forward.png
│ ├── FullBar.png
│ ├── WPTitle.png
│ ├── Background.png
│ ├── RecButtonUp.png
│ ├── RecLightOff.png
│ ├── RecLightOn.png
│ ├── AlignmentGrid.png
│ ├── FilesButtonUp.png
│ ├── RecButtonDown.png
│ ├── RecLightOn_old.png
│ ├── AppIconWithBg300.png
│ ├── ApplicationIcon.png
│ ├── Background-720p.png
│ ├── FilesButtonDown.png
│ ├── RecLightOff_old.png
│ └── Tiles
│ │ ├── IconicTileSmall.png
│ │ ├── FlipCycleTileLarge.png
│ │ ├── FlipCycleTileSmall.png
│ │ ├── FlipCycleTileMedium.png
│ │ └── IconicTileMediumLarge.png
├── Properties
│ ├── AppManifest.xml
│ ├── WMAppManifest.xml
│ └── AssemblyInfo.cs
├── LocalizedStrings.cs
├── App.xaml
├── MainPage.xaml
├── AudioFilePage.xaml
├── Resources
│ ├── AppResources.Designer.cs
│ └── AppResources.resx
├── AudioFileModel.cs
├── XnaAudio.cs
├── AudioModel.cs
├── AudioRecorder.csproj
├── AudioFilePage.xaml.cs
├── AudioManager.cs
├── App.xaml.cs
└── MainPage.xaml.cs
├── WasapiAudioComp
├── pch.h
├── pch.cpp
├── WasapiAudioComp.vcxproj.user
├── WasapiAudioComp.vcxproj.filters
├── WasapiAudio.h
├── WasapiAudioComp.vcxproj
└── WasapiAudio.cpp
├── doc
├── audio_recorder_files_page.png
├── audio_recorder_main_page.png
├── audio_recorder_files_page_small.png
└── audio_recorder_main_page_small.png
├── License.txt
├── README.md
└── AudioRecorder.sln
/AudioRecorder/Assets/Play.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Play.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Save.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Stop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Stop.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Delete.png
--------------------------------------------------------------------------------
/WasapiAudioComp/pch.h:
--------------------------------------------------------------------------------
1 | //
2 | // pch.h
3 | // Header for standard system include files.
4 | //
5 |
6 | #pragma once
7 |
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Forward.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Forward.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/FullBar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/FullBar.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/WPTitle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/WPTitle.png
--------------------------------------------------------------------------------
/doc/audio_recorder_files_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/doc/audio_recorder_files_page.png
--------------------------------------------------------------------------------
/doc/audio_recorder_main_page.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/doc/audio_recorder_main_page.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Background.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/RecButtonUp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/RecButtonUp.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/RecLightOff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/RecLightOff.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/RecLightOn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/RecLightOn.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/AlignmentGrid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/AlignmentGrid.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/FilesButtonUp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/FilesButtonUp.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/RecButtonDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/RecButtonDown.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/RecLightOn_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/RecLightOn_old.png
--------------------------------------------------------------------------------
/doc/audio_recorder_files_page_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/doc/audio_recorder_files_page_small.png
--------------------------------------------------------------------------------
/doc/audio_recorder_main_page_small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/doc/audio_recorder_main_page_small.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/AppIconWithBg300.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/AppIconWithBg300.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/ApplicationIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/ApplicationIcon.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Background-720p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Background-720p.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/FilesButtonDown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/FilesButtonDown.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/RecLightOff_old.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/RecLightOff_old.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Tiles/IconicTileSmall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Tiles/IconicTileSmall.png
--------------------------------------------------------------------------------
/WasapiAudioComp/pch.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // pch.cpp
3 | // Include the standard header and generate the precompiled header.
4 | //
5 |
6 | #include "pch.h"
7 |
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Tiles/FlipCycleTileLarge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Tiles/FlipCycleTileLarge.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Tiles/FlipCycleTileSmall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Tiles/FlipCycleTileSmall.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Tiles/FlipCycleTileMedium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Tiles/FlipCycleTileMedium.png
--------------------------------------------------------------------------------
/AudioRecorder/Assets/Tiles/IconicTileMediumLarge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/audio-recorder/HEAD/AudioRecorder/Assets/Tiles/IconicTileMediumLarge.png
--------------------------------------------------------------------------------
/AudioRecorder/Properties/AppManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/WasapiAudioComp/WasapiAudioComp.vcxproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 |
6 |
--------------------------------------------------------------------------------
/AudioRecorder/LocalizedStrings.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | using AudioRecorder.Resources;
7 |
8 | namespace AudioRecorder
9 | {
10 | ///
11 | /// Provides access to string resources.
12 | ///
13 | public class LocalizedStrings
14 | {
15 | private static AppResources _localizedResources = new AppResources();
16 |
17 | public AppResources LocalizedResources { get { return _localizedResources; } }
18 | }
19 | }
--------------------------------------------------------------------------------
/WasapiAudioComp/WasapiAudioComp.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 5fd0e509-b6ae-4f29-bd2a-4d2cc10f3aa0
6 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/AudioRecorder/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/WasapiAudioComp/WasapiAudio.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | #pragma once
7 |
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | namespace WasapiAudioComp
15 | {
16 | public ref class WasapiAudio sealed
17 | {
18 | public:
19 | WasapiAudio();
20 | virtual ~WasapiAudio();
21 |
22 | bool StartAudioCapture();
23 | bool StopAudioCapture();
24 | int ReadBytes(Platform::Array^* a);
25 |
26 | bool StartAudioRender();
27 | bool StopAudioRender();
28 | void SetAudioBytes(const Platform::Array^ a);
29 | bool Update();
30 | void SkipFiveSecs();
31 |
32 | private:
33 | HRESULT InitCapture();
34 | HRESULT InitRender();
35 |
36 | bool started;
37 | int m_sourceFrameSizeInBytes;
38 |
39 | WAVEFORMATEX* m_waveFormatEx;
40 |
41 | // Device
42 | IAudioClient2* m_pDefaultCaptureDevice;
43 | IAudioClient2* m_pDefaultRenderDevice;
44 |
45 | // Actual capture object
46 | IAudioCaptureClient* m_pCaptureClient;
47 | IAudioRenderClient* m_pRenderClient;
48 |
49 | // TEST AUDIO
50 | BYTE* audioBytes;
51 | long audioIndex;
52 | long audioByteCount;
53 | };
54 | }
--------------------------------------------------------------------------------
/AudioRecorder/Properties/WMAppManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Assets\ApplicationIcon.png
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Assets\Tiles\FlipCycleTileSmall.png
18 | 0
19 | Assets\Tiles\FlipCycleTileMedium.png
20 | Audio Recorder
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/AudioRecorder/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | *
4 | * Nokia, Nokia Connecting People, Nokia Developer, and HERE are trademarks
5 | * and/or registered trademarks of Nokia Corporation. Other product and company
6 | * names mentioned herein may be trademarks or trade names of their respective
7 | * owners.
8 | *
9 | * See the license text file delivered with this project for more information.
10 | */
11 |
12 | using System.Reflection;
13 | using System.Runtime.CompilerServices;
14 | using System.Runtime.InteropServices;
15 | using System.Resources;
16 |
17 | // General Information about an assembly is controlled through the following
18 | // set of attributes. Change these attribute values to modify the information
19 | // associated with an assembly.
20 | [assembly: AssemblyTitle("AudioRecorder")]
21 | [assembly: AssemblyDescription("")]
22 | [assembly: AssemblyConfiguration("")]
23 | [assembly: AssemblyCompany("")]
24 | [assembly: AssemblyProduct("AudioRecorder")]
25 | [assembly: AssemblyCopyright("Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.")]
26 | [assembly: AssemblyTrademark("")]
27 | [assembly: AssemblyCulture("")]
28 |
29 | // Setting ComVisible to false makes the types in this assembly not visible
30 | // to COM components. If you need to access a type in this assembly from
31 | // COM, set the ComVisible attribute to true on that type.
32 | [assembly: ComVisible(false)]
33 |
34 | // The following GUID is for the ID of the typelib if this project is exposed to COM
35 | [assembly: Guid("2802dd8e-fb97-4874-9c7e-bb1e6a77af32")]
36 |
37 | // Version information for an assembly consists of the following four values:
38 | //
39 | // Major Version
40 | // Minor Version
41 | // Build Number
42 | // Revision
43 | //
44 | // You can specify all the values or you can default the Revision and Build Numbers
45 | // by using the '*' as shown below:
46 | [assembly: AssemblyVersion("0.3.0.0")]
47 | [assembly: AssemblyFileVersion("0.3.0.0")]
48 | [assembly: NeutralResourcesLanguageAttribute("en-US")]
49 |
--------------------------------------------------------------------------------
/License.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2013-2014 Microsoft Mobile. All rights reserved.
2 |
3 | Microsoft is a registered trademark of Microsoft Corporation. Nokia and HERE are
4 | trademarks and/or registered trademarks of Nokia Corporation. Other product and
5 | company names mentioned herein may be trademarks or trade names of their
6 | respective owners.
7 |
8 |
9 | License
10 |
11 | Subject to the conditions below, you may use, copy, modify and/or merge copies
12 | of this software and associated content and documentation files (the ”Software”)
13 | to test, develop, publish, distribute, sub-license and/or sell new software
14 | derived from or incorporating the Software, solely in connection with Microsoft
15 | Mobile devices (however branded). Some of the documentation, content and/or
16 | software may be licensed under open source software or other licenses. To the
17 | extent such documentation, content and/or software are included, those licenses
18 | and/or other terms and conditions shall apply in addition and/or instead of this
19 | notice. The exact terms of the licenses, disclaimers, acknowledgements and
20 | notices are reproduced in the materials provided, or in other obvious locations.
21 | No other license to any other intellectual property rights is granted herein.
22 |
23 | This file, unmodified, shall be included with all copies or substantial portions
24 | of the Software that are distributed in source code form.
25 |
26 | The Software cannot constitute the primary value of any new software derived
27 | from or incorporating the Software.
28 |
29 | Any person dealing with the Software shall not misrepresent the source of the
30 | Software.
31 |
32 |
33 | Disclaimer
34 |
35 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
37 | FOR A PARTICULAR PURPOSE, QUALITY AND NONINFRINGEMENT. IN NO EVENT SHALL THE
38 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES (INCLUDING,
39 | WITHOUT LIMITATION, DIRECT, SPECIAL, INDIRECT, PUNITIVE, CONSEQUENTIAL,
40 | EXEMPLARY AND/ OR INCIDENTAL DAMAGES) OR OTHER LIABILITY, WHETHER IN AN ACTION
41 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
42 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
43 |
44 | Microsoft Mobile Oy retains the right to make changes to this document at any
45 | time, without notice.
46 |
--------------------------------------------------------------------------------
/AudioRecorder/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | AudioRecorder
2 | =============
3 |
4 | AudioRecorder example application demonstrates how to record and play audio on
5 | Windows Phone 8 devices. XNA Framework Audio API and Windows Audio Session API
6 | (WASAPI) are covered by the application.
7 |
8 | 
9 | 
10 |
11 | This example application is hosted in GitHub:
12 | https://github.com/Microsoft/audio-recorder
13 |
14 | Developed with:
15 |
16 | * Microsoft Visual Studio Express for Windows Phone 2012.
17 |
18 | Compatible with:
19 |
20 | * Windows Phone 8
21 |
22 | Tested to work on:
23 |
24 | * Nokia Lumia 920
25 | * Nokia Lumia 925
26 | * Nokia Lumia 1520
27 |
28 |
29 | Instructions
30 | ------------
31 |
32 | Make sure you have the following installed:
33 |
34 | * Windows 8
35 | * Windows Phone SDK 8.0
36 |
37 | To build and run the sample:
38 |
39 | * Open the SLN file
40 | * File > Open Project, select the file AudioRecorder.sln
41 | * Select the target, for example 'Emulator WXGA'.
42 | * Press F5 to build the project and run it on the Windows Phone Emulator.
43 |
44 | To deploy the sample on Windows Phone device:
45 | * See the official documentation for deploying and testing applications on
46 | Windows Phone devices at http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff402565(v=vs.105).aspx
47 |
48 |
49 | About the implementation
50 | ------------------------
51 |
52 | Important folders:
53 |
54 | | Folder | Description |
55 | | ------ | ----------- |
56 | | / | Contains the project file, the license information and this file (README.md) |
57 | | AudioRecorder | Root folder for the implementation files. |
58 | | AudioRecorder/Assets | Graphic assets like icons and tiles. |
59 | | AudioRecorder/Properties | Application property files. |
60 | | AudioRecorder/Resources | Application resources. |
61 | | WasapiAudioComp | Root folder of Windows Phone Runtime component for WASAPI implementation files. |
62 |
63 |
64 | Important classes:
65 |
66 | | File | Description |
67 | | ---- | ----------- |
68 | | MainPage | This class is the main UI of the app. |
69 | | AudioManager | Handles all the UI related audio actions. |
70 | | XnaAudio | Handles the recording and playback of audio using XNA Audio API. |
71 | | WasapiAudio | Handles the audio capturing and rendering using WASAPI. |
72 |
73 |
74 | For more information about audio handling in Windows Phone 8 devices, see an
75 | article available at http://developer.nokia.com/Community/Wiki/Audio_recording_and_playback_options_in_Windows_Phone.
76 |
77 |
78 | Known issues
79 | ------------
80 |
81 | No known issues.
82 |
83 |
84 | License
85 | -------
86 |
87 | See the license file delivered with this project.
88 | The license is also available online at
89 | https://github.com/Microsoft/audio-recorder/blob/master/License.txt
90 |
91 |
92 | Version history
93 | ---------------
94 |
95 | * 0.3.0.0 Added 720p resolution support and yet another missing dependency fix.
96 | * 0.2.0.0 Added a missing dependency affecting others than ARM device builds.
97 | * 0.1.0.0 First beta release.
98 |
--------------------------------------------------------------------------------
/AudioRecorder/AudioFilePage.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/AudioRecorder/Resources/AppResources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.17626
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace AudioRecorder.Resources
12 | {
13 | using System;
14 |
15 |
16 | ///
17 | /// A strongly-typed resource class, for looking up localized strings, etc.
18 | ///
19 | // This class was auto-generated by the StronglyTypedResourceBuilder
20 | // class via a tool like ResGen or Visual Studio.
21 | // To add or remove a member, edit your .ResX file then rerun ResGen
22 | // with the /str option, or rebuild your VS project.
23 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
24 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
25 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
26 | public class AppResources
27 | {
28 |
29 | private static global::System.Resources.ResourceManager resourceMan;
30 |
31 | private static global::System.Globalization.CultureInfo resourceCulture;
32 |
33 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
34 | internal AppResources()
35 | {
36 | }
37 |
38 | ///
39 | /// Returns the cached ResourceManager instance used by this class.
40 | ///
41 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
42 | public static global::System.Resources.ResourceManager ResourceManager
43 | {
44 | get
45 | {
46 | if (object.ReferenceEquals(resourceMan, null))
47 | {
48 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AudioRecorder.Resources.AppResources", typeof(AppResources).Assembly);
49 | resourceMan = temp;
50 | }
51 | return resourceMan;
52 | }
53 | }
54 |
55 | ///
56 | /// Overrides the current thread's CurrentUICulture property for all
57 | /// resource lookups using this strongly typed resource class.
58 | ///
59 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
60 | public static global::System.Globalization.CultureInfo Culture
61 | {
62 | get
63 | {
64 | return resourceCulture;
65 | }
66 | set
67 | {
68 | resourceCulture = value;
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized string similar to LeftToRight.
74 | ///
75 | public static string ResourceFlowDirection
76 | {
77 | get
78 | {
79 | return ResourceManager.GetString("ResourceFlowDirection", resourceCulture);
80 | }
81 | }
82 |
83 | ///
84 | /// Looks up a localized string similar to us-EN.
85 | ///
86 | public static string ResourceLanguage
87 | {
88 | get
89 | {
90 | return ResourceManager.GetString("ResourceLanguage", resourceCulture);
91 | }
92 | }
93 |
94 | ///
95 | /// Looks up a localized string similar to MY APPLICATION.
96 | ///
97 | public static string ApplicationTitle
98 | {
99 | get
100 | {
101 | return ResourceManager.GetString("ApplicationTitle", resourceCulture);
102 | }
103 | }
104 |
105 | ///
106 | /// Looks up a localized string similar to button.
107 | ///
108 | public static string AppBarButtonText
109 | {
110 | get
111 | {
112 | return ResourceManager.GetString("AppBarButtonText", resourceCulture);
113 | }
114 | }
115 |
116 | ///
117 | /// Looks up a localized string similar to menu item.
118 | ///
119 | public static string AppBarMenuItemText
120 | {
121 | get
122 | {
123 | return ResourceManager.GetString("AppBarMenuItemText", resourceCulture);
124 | }
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/AudioRecorder.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Express 2012 for Windows Phone
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AudioRecorder", "AudioRecorder\AudioRecorder.csproj", "{32950044-1799-4102-A4C7-8FF477B5B081}"
4 | EndProject
5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WasapiAudioComp", "WasapiAudioComp\WasapiAudioComp.vcxproj", "{18887186-0D7B-4441-B224-CADAC3B59CA9}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|Any CPU = Debug|Any CPU
10 | Debug|ARM = Debug|ARM
11 | Debug|Mixed Platforms = Debug|Mixed Platforms
12 | Debug|Win32 = Debug|Win32
13 | Debug|x86 = Debug|x86
14 | Release|Any CPU = Release|Any CPU
15 | Release|ARM = Release|ARM
16 | Release|Mixed Platforms = Release|Mixed Platforms
17 | Release|Win32 = Release|Win32
18 | Release|x86 = Release|x86
19 | EndGlobalSection
20 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
21 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
22 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Any CPU.Build.0 = Debug|Any CPU
23 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
24 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|ARM.ActiveCfg = Debug|ARM
25 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|ARM.Build.0 = Debug|ARM
26 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|ARM.Deploy.0 = Debug|ARM
27 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
28 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Mixed Platforms.Build.0 = Debug|x86
29 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Mixed Platforms.Deploy.0 = Debug|x86
30 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Win32.ActiveCfg = Debug|x86
31 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Win32.Build.0 = Debug|x86
32 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|Win32.Deploy.0 = Debug|x86
33 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|x86.ActiveCfg = Debug|x86
34 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|x86.Build.0 = Debug|x86
35 | {32950044-1799-4102-A4C7-8FF477B5B081}.Debug|x86.Deploy.0 = Debug|x86
36 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Any CPU.Build.0 = Release|Any CPU
38 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Any CPU.Deploy.0 = Release|Any CPU
39 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|ARM.ActiveCfg = Release|ARM
40 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|ARM.Build.0 = Release|ARM
41 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|ARM.Deploy.0 = Release|ARM
42 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Mixed Platforms.ActiveCfg = Release|x86
43 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Mixed Platforms.Build.0 = Release|x86
44 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Mixed Platforms.Deploy.0 = Release|x86
45 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Win32.ActiveCfg = Release|x86
46 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Win32.Build.0 = Release|x86
47 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|Win32.Deploy.0 = Release|x86
48 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|x86.ActiveCfg = Release|x86
49 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|x86.Build.0 = Release|x86
50 | {32950044-1799-4102-A4C7-8FF477B5B081}.Release|x86.Deploy.0 = Release|x86
51 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|Any CPU.ActiveCfg = Debug|Win32
52 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|ARM.ActiveCfg = Debug|ARM
53 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|ARM.Build.0 = Debug|ARM
54 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
55 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|Mixed Platforms.Build.0 = Debug|Win32
56 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|Win32.ActiveCfg = Debug|Win32
57 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|Win32.Build.0 = Debug|Win32
58 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|x86.ActiveCfg = Debug|Win32
59 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Debug|x86.Build.0 = Debug|Win32
60 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|Any CPU.ActiveCfg = Release|Win32
61 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|ARM.ActiveCfg = Release|ARM
62 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|ARM.Build.0 = Release|ARM
63 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|Mixed Platforms.ActiveCfg = Release|Win32
64 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|Mixed Platforms.Build.0 = Release|Win32
65 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|Win32.ActiveCfg = Release|Win32
66 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|Win32.Build.0 = Release|Win32
67 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|x86.ActiveCfg = Release|Win32
68 | {18887186-0D7B-4441-B224-CADAC3B59CA9}.Release|x86.Build.0 = Release|Win32
69 | EndGlobalSection
70 | GlobalSection(SolutionProperties) = preSolution
71 | HideSolutionNode = FALSE
72 | EndGlobalSection
73 | EndGlobal
74 |
--------------------------------------------------------------------------------
/AudioRecorder/AudioFileModel.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.ComponentModel;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace AudioRecorder
14 | {
15 | ///
16 | /// Data model of an audio file.
17 | ///
18 | public class AudioFileModel : INotifyPropertyChanged
19 | {
20 | private string _fileName;
21 | ///
22 | /// Audio file name property.
23 | /// This property is used in the UI to display its value using a Binding.
24 | ///
25 | public string FileName
26 | {
27 | get
28 | {
29 | return _fileName;
30 | }
31 | set
32 | {
33 | if (value != _fileName)
34 | {
35 | _fileName = value;
36 | NotifyPropertyChanged("FileName");
37 | }
38 | }
39 | }
40 |
41 | private string _fileSize;
42 | ///
43 | /// Audio file size property.
44 | /// This property is used in the UI to display its value using a Binding.
45 | ///
46 | public string FileSize
47 | {
48 | get
49 | {
50 | return _fileSize;
51 | }
52 | set
53 | {
54 | if (value != _fileSize)
55 | {
56 | _fileSize = value;
57 | NotifyPropertyChanged("FileSize");
58 | }
59 | }
60 | }
61 |
62 | private int _channelCount;
63 | ///
64 | /// Audio file channel count property.
65 | /// This property is used in the UI to display its value using a Binding.
66 | ///
67 | public int ChannelCount
68 | {
69 | get
70 | {
71 | return _channelCount;
72 | }
73 | set
74 | {
75 | if (value != _channelCount)
76 | {
77 | _channelCount = value;
78 | ChannelCountString = value == 1 ? "Mono" : "Stereo";
79 | NotifyPropertyChanged("ChannelCount");
80 | }
81 | }
82 | }
83 |
84 | private string _channelCountString;
85 | ///
86 | /// Audio file channel count string property.
87 | /// This property is used in the UI to display its value using a Binding.
88 | ///
89 | public string ChannelCountString
90 | {
91 | get
92 | {
93 | return _channelCountString;
94 | }
95 | set
96 | {
97 | if (value != _channelCountString)
98 | {
99 | _channelCountString = value;
100 | NotifyPropertyChanged("ChannelCountString");
101 | }
102 | }
103 | }
104 |
105 | private int _sampleRate;
106 | ///
107 | /// Audio file sample rate property.
108 | /// This property is used in the UI to display its value using a Binding.
109 | ///
110 | public int SampleRate
111 | {
112 | get
113 | {
114 | return _sampleRate;
115 | }
116 | set
117 | {
118 | if (value != _sampleRate)
119 | {
120 | _sampleRate = value;
121 | SampleRateString = value + " Hz";
122 | NotifyPropertyChanged("SampleRate");
123 | }
124 | }
125 | }
126 |
127 | private string _sampleRateString;
128 | ///
129 | /// Audio file sample rate string property.
130 | /// This property is used in the UI to display its value using a Binding.
131 | ///
132 | public string SampleRateString
133 | {
134 | get
135 | {
136 | return _sampleRateString;
137 | }
138 | set
139 | {
140 | if (value != _sampleRateString)
141 | {
142 | _sampleRateString = value;
143 | NotifyPropertyChanged("SampleRateString");
144 | }
145 | }
146 | }
147 |
148 | private string _fileLength;
149 | ///
150 | /// Audio file length property.
151 | /// This property is used in the UI to display its value using a Binding.
152 | ///
153 | public string FileLength
154 | {
155 | get
156 | {
157 | return _fileLength;
158 | }
159 | set
160 | {
161 | if (value != _fileLength)
162 | {
163 | _fileLength = value;
164 | NotifyPropertyChanged("FileLength");
165 | }
166 | }
167 | }
168 |
169 | public event PropertyChangedEventHandler PropertyChanged;
170 | private void NotifyPropertyChanged(String propertyName)
171 | {
172 | PropertyChangedEventHandler handler = PropertyChanged;
173 | if (null != handler)
174 | {
175 | handler(this, new PropertyChangedEventArgs(propertyName));
176 | }
177 | }
178 | }
179 | }
180 |
--------------------------------------------------------------------------------
/AudioRecorder/XnaAudio.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | using Microsoft.Xna.Framework;
7 | using Microsoft.Xna.Framework.Audio;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.IO;
11 | using System.IO.IsolatedStorage;
12 | using System.Linq;
13 | using System.Text;
14 | using System.Threading;
15 | using System.Threading.Tasks;
16 | using System.Windows;
17 | using System.Windows.Threading;
18 |
19 | namespace AudioRecorder
20 | {
21 | ///
22 | /// Class handling the XNA API to record and play audio.
23 | ///
24 | public class XnaAudio
25 | {
26 | private Microphone microphone = Microphone.Default; // Object representing the physical microphone on the device
27 | private byte[] buffer; // Dynamic buffer to retrieve audio data from the microphone
28 | private DynamicSoundEffectInstance playback; // Used to play back audio
29 | private int sampleSize;
30 | private int position;
31 | private byte[] byteArray;
32 |
33 | ///
34 | /// Constructor.
35 | ///
36 | public XnaAudio()
37 | {
38 | // Event handler for getting audio data when the buffer is full
39 | microphone.BufferDuration = TimeSpan.FromMilliseconds(100);
40 | microphone.BufferReady += new EventHandler(microphone_BufferReady);
41 |
42 | // initialize dynamic sound effect instance
43 | playback = new DynamicSoundEffectInstance(microphone.SampleRate, AudioChannels.Mono);
44 | playback.BufferNeeded += GetSamples;
45 | sampleSize = playback.GetSampleSizeInBytes(TimeSpan.FromMilliseconds(100));
46 | }
47 |
48 | ///
49 | /// Gives samples to the DynamicSoundEffectInstance to play.
50 | /// Updates the position of the playback in the buffer.
51 | ///
52 | /// DynamicSoundEffectInstance.
53 | /// Event arguments.
54 | private void GetSamples(object sender, EventArgs e)
55 | {
56 | // -44 for taking wav header into account
57 | while (playback.PendingBufferCount < 2 && position < byteArray.Length - 44)
58 | {
59 | playback.SubmitBuffer(byteArray, position, sampleSize);
60 | position += sampleSize;
61 | }
62 | }
63 |
64 | ///
65 | /// Checks if there is an audio still playing.
66 | ///
67 | /// True if an audio is being played.
68 | public Boolean SoundIsPlaying()
69 | {
70 | Boolean ret = true;
71 | if (playback.PendingBufferCount == 0)
72 | {
73 | ret = false;
74 | }
75 | return ret;
76 | }
77 |
78 | ///
79 | /// The Microphone.BufferReady event handler.
80 | /// Gets the audio data from the microphone, stores it in a buffer,
81 | /// then writes that buffer to a stream for later playback.
82 | /// Any action in this event handler should be quick!
83 | ///
84 | ///
85 | ///
86 | void microphone_BufferReady(object sender, EventArgs e)
87 | {
88 | // Retrieve audio data
89 | microphone.GetData(buffer);
90 |
91 | // send data to model, for visualization
92 | App.AudioModel.AudioBuffer = buffer;
93 |
94 | // Store the audio data in a stream
95 | App.AudioModel.stream.Write(buffer, 0, buffer.Length);
96 | }
97 |
98 | ///
99 | /// Plays the audio using DynamicSoundEffectInstance
100 | /// so we can monitor the playback status.
101 | ///
102 | private void playSoundEffect()
103 | {
104 | playback.Play();
105 | }
106 |
107 | ///
108 | /// Start playback from stream.
109 | ///
110 | /// Name of the file to play.
111 | public void StartPlayback(String fileName)
112 | {
113 | App.AudioModel.LoadAudioBuffer(fileName);
114 |
115 | if (App.AudioModel.stream.Length > 0)
116 | {
117 | App.AudioModel.stream.Position = 0;
118 | position = 44; // take wav header into account
119 | byteArray = App.AudioModel.stream.ToArray();
120 |
121 | // Play the audio in a new thread so the UI can update.
122 | Thread soundThread = new Thread(new ThreadStart(playSoundEffect));
123 | soundThread.Start();
124 | }
125 | }
126 |
127 | ///
128 | /// Stops playback.
129 | ///
130 | public void StopPlayback()
131 | {
132 | if (playback.State == SoundState.Playing)
133 | {
134 | playback.Stop();
135 | }
136 | }
137 |
138 | ///
139 | /// Start recording.
140 | ///
141 | public void StartRecording()
142 | {
143 | // Get audio data in 100 ms chunks
144 | microphone.BufferDuration = TimeSpan.FromMilliseconds(100);
145 |
146 | // Allocate memory to hold the audio data
147 | buffer = new byte[microphone.GetSampleSizeInBytes(microphone.BufferDuration)];
148 |
149 | // Set the stream back to zero in case there is already something in it
150 | App.AudioModel.stream.SetLength(0);
151 |
152 | // Start recording
153 | microphone.Start();
154 | }
155 |
156 | ///
157 | /// Stop recording.
158 | ///
159 | public void StopRecording()
160 | {
161 | if (microphone.State == MicrophoneState.Started)
162 | {
163 | microphone.Stop();
164 | }
165 | }
166 |
167 | ///
168 | /// Skips next five seconds of playback if possible.
169 | ///
170 | public void SkipFiveSecs()
171 | {
172 | if (playback.State == SoundState.Playing)
173 | {
174 | if (position + sampleSize * 50 < byteArray.Length)
175 | {
176 | position += sampleSize * 50;
177 | }
178 | }
179 | }
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/AudioRecorder/Resources/AppResources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | LeftToRight
122 | Controls the FlowDirection for all elements in the RootFrame. Set to the traditional direction of this resource file's language
123 |
124 |
125 | en-US
126 | Controls the Language and ensures that the font for all elements in the RootFrame aligns with the app's language. Set to the language code of this resource file's language.
127 |
128 |
129 | MY APPLICATION
130 |
131 |
132 | add
133 |
134 |
135 | Menu Item
136 |
137 |
--------------------------------------------------------------------------------
/WasapiAudioComp/WasapiAudioComp.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Debug
10 | ARM
11 |
12 |
13 | Release
14 | Win32
15 |
16 |
17 | Release
18 | ARM
19 |
20 |
21 |
22 | {18887186-0d7b-4441-b224-cadac3b59ca9}
23 | WasapiAudioComp
24 | en-US
25 | 11.0
26 | true
27 |
28 |
29 |
30 | DynamicLibrary
31 | true
32 | v110_wp80
33 |
34 |
35 | DynamicLibrary
36 | true
37 | v110_wp80
38 |
39 |
40 | DynamicLibrary
41 | false
42 | true
43 | v110_wp80
44 |
45 |
46 | DynamicLibrary
47 | false
48 | true
49 | v110_wp80
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 | false
58 |
59 |
60 |
61 | _WINRT_DLL;%(PreprocessorDefinitions)
62 | Use
63 | pch.h
64 | $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)
65 | true
66 |
67 |
68 | Console
69 | false
70 | ole32.lib;%(IgnoreSpecificDefaultLibraries)
71 | true
72 | WindowsPhoneCore.lib;PhoneAudioSes.lib;RuntimeObject.lib;PhoneAppModelHost.lib;%(AdditionalDependencies)
73 |
74 |
75 |
76 |
77 | _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)
78 | Use
79 | pch.h
80 | $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)
81 | true
82 |
83 |
84 | Console
85 | false
86 | ole32.lib;%(IgnoreSpecificDefaultLibraries)
87 | true
88 | WindowsPhoneCore.lib;PhoneAudioSes.lib;RuntimeObject.lib;PhoneAppModelHost.lib;%(AdditionalDependencies)
89 |
90 |
91 |
92 |
93 | _WINRT_DLL;%(PreprocessorDefinitions)
94 | Use
95 | pch.h
96 | $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)
97 | true
98 |
99 |
100 | Console
101 | false
102 | ole32.lib;%(IgnoreSpecificDefaultLibraries)
103 | true
104 | WindowsPhoneCore.lib;PhoneAudioSes.lib;RuntimeObject.lib;PhoneAppModelHost.lib;%(AdditionalDependencies)
105 |
106 |
107 |
108 |
109 | _WINRT_DLL;NDEBUG;%(PreprocessorDefinitions)
110 | Use
111 | pch.h
112 | $(WindowsSDK_MetadataPath);$(AdditionalUsingDirectories)
113 | true
114 |
115 |
116 | Console
117 | false
118 | ole32.lib;%(IgnoreSpecificDefaultLibraries)
119 | true
120 | WindowsPhoneCore.lib;PhoneAudioSes.lib;RuntimeObject.lib;PhoneAppModelHost.lib;%(AdditionalDependencies)
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 | true
130 | false
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | Create
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/AudioRecorder/AudioModel.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Collections.ObjectModel;
9 | using System.ComponentModel;
10 | using System.IO;
11 | using System.IO.IsolatedStorage;
12 | using System.Linq;
13 | using System.Text;
14 | using System.Threading.Tasks;
15 | using System.Windows;
16 |
17 | namespace AudioRecorder
18 | {
19 | // Delegate types for hooking up change notifications.
20 | public delegate void AudioBufferChangedEventHandler(object sender, EventArgs e);
21 | public delegate void PlayingEventHandler(object sender, EventArgs e);
22 |
23 | ///
24 | /// Data model to hold audio state information.
25 | ///
26 | public class AudioModel : INotifyPropertyChanged
27 | {
28 | // Members
29 | public ObservableCollection AudioFiles { get; private set; }
30 | public MemoryStream stream = new MemoryStream(); // Stream to hold audio data
31 |
32 | ///
33 | /// AudioBuffer property.
34 | /// Use this property to access current audio buffer.
35 | ///
36 | private byte[] _audioBuffer;
37 | public byte[] AudioBuffer
38 | {
39 | get
40 | {
41 | return _audioBuffer;
42 | }
43 | set
44 | {
45 | _audioBuffer = value;
46 | OnAudioBufferChanged(EventArgs.Empty);
47 | }
48 | }
49 |
50 | // An event used to notify clients whenever the audio buffer changes.
51 | public event AudioBufferChangedEventHandler AudioBufferChanged;
52 |
53 | protected virtual void OnAudioBufferChanged(EventArgs e)
54 | {
55 | if (AudioBufferChanged != null)
56 | {
57 | AudioBufferChanged(this, e);
58 | }
59 | }
60 |
61 | ///
62 | /// IsPlaying property.
63 | /// Use this property to determine if audio is being played.
64 | ///
65 | private Boolean _isPlaying;
66 | public Boolean IsPlaying
67 | {
68 | get
69 | {
70 | return _isPlaying;
71 | }
72 | set
73 | {
74 | if (_isPlaying != value)
75 | {
76 | _isPlaying = value;
77 | OnPlayingChanged(EventArgs.Empty);
78 | }
79 | }
80 | }
81 |
82 | // An event used to notify clients whenever the playing state changes.
83 | public event PlayingEventHandler PlayingChanged;
84 |
85 | protected virtual void OnPlayingChanged(EventArgs e)
86 | {
87 | if (PlayingChanged != null)
88 | {
89 | PlayingChanged(this, e);
90 | }
91 | }
92 |
93 | ///
94 | /// IsRecording property.
95 | /// Use this property to determine if audio is being recorded.
96 | ///
97 | private Boolean _isRecording = false;
98 | public Boolean IsRecording
99 | {
100 | get
101 | {
102 | return _isRecording;
103 | }
104 | set
105 | {
106 | if (value != _isRecording)
107 | {
108 | _isRecording = value;
109 | NotifyPropertyChanged("IsRecording");
110 | }
111 | }
112 | }
113 |
114 | ///
115 | /// ChannelCount property.
116 | /// Use this property to determine whether mono or stereo is recorded.
117 | ///
118 | private int _channelCount = 1;
119 | public int ChannelCount
120 | {
121 | get
122 | {
123 | return _channelCount;
124 | }
125 | set
126 | {
127 | if (value != _channelCount)
128 | {
129 | _channelCount = value;
130 | NotifyPropertyChanged("ChannelCount");
131 | }
132 | }
133 | }
134 |
135 | ///
136 | /// SampleRate property.
137 | /// Use this property to determine the sample rate used in recording.
138 | ///
139 | private int _sampleRate = 16000;
140 | public int SampleRate
141 | {
142 | get
143 | {
144 | return _sampleRate;
145 | }
146 | set
147 | {
148 | if (value != _sampleRate)
149 | {
150 | _sampleRate = value;
151 | NotifyPropertyChanged("SampleRate");
152 | }
153 | }
154 | }
155 |
156 | public event PropertyChangedEventHandler PropertyChanged;
157 | private void NotifyPropertyChanged(String propertyName)
158 | {
159 | PropertyChangedEventHandler handler = PropertyChanged;
160 | if (null != handler)
161 | {
162 | handler(this, new PropertyChangedEventArgs(propertyName));
163 | }
164 | }
165 |
166 | ///
167 | /// Constructor.
168 | ///
169 | public AudioModel()
170 | {
171 | AudioFiles = new ObservableCollection();
172 | }
173 |
174 | ///
175 | /// Loads local audio information.
176 | ///
177 | public void ReadAudioFileInfo()
178 | {
179 | AudioFiles.Clear();
180 |
181 | // Load the image which was filtered from isolated app storage.
182 | System.IO.IsolatedStorage.IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication();
183 |
184 | try
185 | {
186 | string[] fileNames = myStore.GetFileNames();
187 | foreach (string s in fileNames)
188 | {
189 | AudioFileModel audioFile = new AudioFileModel();
190 | audioFile.FileName = s;
191 | IsolatedStorageFileStream fileStream = myStore.OpenFile(s, System.IO.FileMode.Open, System.IO.FileAccess.Read);
192 | audioFile.FileSize = "" + fileStream.Length + " bytes";
193 |
194 | // Read sample rate and channel count
195 | Encoding encoding = Encoding.UTF8;
196 | byte[] bytes = new byte[4];
197 |
198 | // channel count
199 | fileStream.Seek(22, SeekOrigin.Begin);
200 | fileStream.Read(bytes, 0, 2);
201 | audioFile.ChannelCount = BitConverter.ToInt16(bytes, 0);
202 |
203 | // sample rate
204 | fileStream.Read(bytes, 0, 4);
205 | audioFile.SampleRate = BitConverter.ToInt32(bytes, 0);
206 |
207 | audioFile.FileLength = "" + fileStream.Length / (2 * audioFile.SampleRate * audioFile.ChannelCount) + " seconds";
208 | AudioFiles.Add(audioFile);
209 |
210 | fileStream.Dispose();
211 | }
212 | }
213 | catch
214 | {
215 | MessageBox.Show("Error while trying to read audio files.");
216 | }
217 | }
218 |
219 | ///
220 | /// Loads an audio buffer from isolated storage.
221 | ///
222 | public void LoadAudioBuffer(String fileName)
223 | {
224 | // Set the stream back to zero in case there is already something in it
225 | stream.SetLength(0);
226 |
227 | // Retrieve the named audio file from the storage.
228 | IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication();
229 |
230 | try
231 | {
232 | using (var isoFileStream = new IsolatedStorageFileStream(
233 | fileName,
234 | FileMode.Open,
235 | myStore))
236 | {
237 | isoFileStream.CopyTo(stream);
238 | stream.Flush();
239 | }
240 | }
241 | catch
242 | {
243 | MessageBox.Show("Error while trying to load audio buffer.");
244 | }
245 | }
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/AudioRecorder/AudioRecorder.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 10.0.20506
7 | 2.0
8 | {32950044-1799-4102-A4C7-8FF477B5B081}
9 | {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
10 | Library
11 | Properties
12 | AudioRecorder
13 | AudioRecorder
14 | WindowsPhone
15 | v8.0
16 | $(TargetFrameworkVersion)
17 | true
18 |
19 |
20 | true
21 | true
22 | AudioRecorder_$(Configuration)_$(Platform).xap
23 | Properties\AppManifest.xml
24 | AudioRecorder.App
25 | true
26 | 11.0
27 | true
28 |
29 |
30 | true
31 | full
32 | false
33 | Bin\Debug
34 | DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE
35 | true
36 | true
37 | prompt
38 | 4
39 |
40 |
41 | pdbonly
42 | true
43 | Bin\Release
44 | TRACE;SILVERLIGHT;WINDOWS_PHONE
45 | true
46 | true
47 | prompt
48 | 4
49 |
50 |
51 | true
52 | full
53 | false
54 | Bin\x86\Debug
55 | DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE
56 | true
57 | true
58 | prompt
59 | 4
60 |
61 |
62 | pdbonly
63 | true
64 | Bin\x86\Release
65 | TRACE;SILVERLIGHT;WINDOWS_PHONE
66 | true
67 | true
68 | prompt
69 | 4
70 |
71 |
72 | true
73 | full
74 | false
75 | Bin\ARM\Debug
76 | DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE
77 | true
78 | true
79 | prompt
80 | 4
81 |
82 |
83 | pdbonly
84 | true
85 | Bin\ARM\Release
86 | TRACE;SILVERLIGHT;WINDOWS_PHONE
87 | true
88 | true
89 | prompt
90 | 4
91 |
92 |
93 |
94 | App.xaml
95 |
96 |
97 |
98 | AudioFilePage.xaml
99 |
100 |
101 |
102 |
103 |
104 | MainPage.xaml
105 |
106 |
107 |
108 | True
109 | True
110 | AppResources.resx
111 |
112 |
113 |
114 |
115 |
116 | Designer
117 | MSBuild:Compile
118 |
119 |
120 | Designer
121 |
122 |
123 | Designer
124 | MSBuild:Compile
125 |
126 |
127 |
128 |
129 |
130 | Designer
131 |
132 |
133 |
134 |
135 |
136 | PreserveNewest
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 | PreserveNewest
154 |
155 |
156 | PreserveNewest
157 |
158 |
159 | PreserveNewest
160 |
161 |
162 | PreserveNewest
163 |
164 |
165 | PreserveNewest
166 |
167 |
168 |
169 |
170 |
171 | PublicResXFileCodeGenerator
172 | AppResources.Designer.cs
173 |
174 |
175 |
176 |
177 | {18887186-0D7B-4441-B224-CADAC3B59CA9}
178 | WasapiAudioComp
179 |
180 |
181 |
182 |
183 |
190 |
191 |
--------------------------------------------------------------------------------
/AudioRecorder/AudioFilePage.xaml.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Net;
10 | using System.Windows;
11 | using System.Windows.Controls;
12 | using System.Windows.Navigation;
13 | using Microsoft.Phone.Controls;
14 | using Microsoft.Phone.Shell;
15 | using System.IO.IsolatedStorage;
16 | using System.IO;
17 | using Microsoft.Xna.Framework.Media;
18 | using Microsoft.Xna.Framework.Media.PhoneExtensions;
19 |
20 | namespace AudioRecorder
21 | {
22 | ///
23 | /// Page for displaying audio files in isolated storage.
24 | ///
25 | public partial class AudioFilePage : PhoneApplicationPage
26 | {
27 | // Members
28 | String selectedFileName = "";
29 |
30 | ///
31 | /// Constructor.
32 | ///
33 | public AudioFilePage()
34 | {
35 | InitializeComponent();
36 | App.AudioModel.ReadAudioFileInfo();
37 | DataContext = App.AudioModel;
38 |
39 | CreateAppBar();
40 | App.AudioModel.PlayingChanged += new PlayingEventHandler(PlayingChanged);
41 | }
42 |
43 | ///
44 | /// Stop the playback when navigating away from the page.
45 | ///
46 | /// Event arguments.
47 | protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
48 | {
49 | if (App.AudioModel.IsPlaying)
50 | {
51 | App.AudioManager.StopPlayback();
52 | }
53 |
54 | base.OnNavigatingFrom(e);
55 | }
56 |
57 | ///
58 | /// Called whenever the audio play mode changes.
59 | ///
60 | /// AudioModel.
61 | /// Event arguments.
62 | private void PlayingChanged(object sender, EventArgs e)
63 | {
64 | if (App.AudioModel.IsPlaying)
65 | {
66 | StopPlayButton.IconUri = new Uri("Assets/Stop.png", UriKind.Relative);
67 | StopPlayButton.Text = "stop";
68 | }
69 | else
70 | {
71 | StopPlayButton.IconUri = new Uri("Assets/Play.png", UriKind.Relative);
72 | StopPlayButton.Text = "play";
73 | }
74 | ForwardButton.IsEnabled = App.AudioModel.IsPlaying;
75 | }
76 |
77 | ///
78 | /// Called when an audio file is selected.
79 | ///
80 | /// AudioFilesList (LongListSelector).
81 | /// Event arguments.
82 | void OnAudioFileSelected(Object sender, SelectionChangedEventArgs e)
83 | {
84 | AudioFileModel selected = (AudioFileModel)AudioFilesList.SelectedItem;
85 |
86 | if (App.AudioModel.IsPlaying)
87 | {
88 | App.AudioManager.StopPlayback();
89 | }
90 |
91 | if (selected != null)
92 | {
93 | selectedFileName = selected.FileName;
94 |
95 | StopPlayButton.IsEnabled = true;
96 | SaveButton.IsEnabled = true;
97 | DeleteButton.IsEnabled = true;
98 | }
99 | }
100 |
101 | ///
102 | /// Start/Stop the audio playback.
103 | ///
104 | /// StopPlay menu item
105 | /// Event arguments
106 | private void StopPlay_Click(object sender, EventArgs e)
107 | {
108 | if (App.AudioModel.IsPlaying)
109 | {
110 | App.AudioManager.StopPlayback();
111 | }
112 | else
113 | {
114 | if (selectedFileName.Length == 0)
115 | {
116 | MessageBox.Show("Select an audio file first.");
117 | }
118 | else
119 | {
120 | AudioFileModel selected = (AudioFileModel)AudioFilesList.SelectedItem;
121 | Boolean useWasapi = selected.SampleRate != 16000 || selected.ChannelCount != 1;
122 | App.AudioManager.StartPlayback(selectedFileName, useWasapi);
123 | }
124 | }
125 | }
126 |
127 | ///
128 | /// Save the audio file to media library.
129 | ///
130 | /// Save menu item
131 | /// Event arguments
132 | private void Save_Click(object sender, EventArgs e)
133 | {
134 | AudioFileModel selected = (AudioFileModel)AudioFilesList.SelectedItem;
135 | if (selected != null)
136 | {
137 | var library = new MediaLibrary();
138 | Song s = library.SaveSong(
139 | new Uri(selected.FileName, UriKind.RelativeOrAbsolute),
140 | null,
141 | /*
142 | new SongMetadata()
143 | {
144 | ArtistName = "ArtistName",
145 | AlbumArtistName = "AlbumArtistName",
146 | Name = "SongName",
147 | AlbumName = "AlbumName",
148 | Duration = TimeSpan.FromSeconds(10),
149 | TrackNumber = 1,
150 | AlbumReleaseDate = DateTime.Now,
151 | GenreName = "Genre"
152 | },
153 | */
154 | SaveSongOperation.CopyToLibrary);
155 | }
156 | }
157 |
158 | ///
159 | /// Delete the audio file.
160 | ///
161 | /// Delete menu item
162 | /// Event arguments
163 | private void Delete_Click(object sender, EventArgs e)
164 | {
165 | AudioFileModel selected = (AudioFileModel)AudioFilesList.SelectedItem;
166 | if (selected != null)
167 | {
168 | IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication();
169 | try
170 | {
171 | myStore.DeleteFile(selected.FileName);
172 | App.AudioModel.AudioFiles.Remove(selected);
173 | selectedFileName = "";
174 | }
175 | catch
176 | {
177 | MessageBox.Show("Error while deleting audio file " + selected.FileName + ".");
178 | }
179 |
180 | StopPlayButton.IsEnabled = false;
181 | SaveButton.IsEnabled = false;
182 | DeleteButton.IsEnabled = false;
183 | ForwardButton.IsEnabled = false;
184 | }
185 | }
186 |
187 | ///
188 | /// Skip next 5 secs of the audio playback.
189 | ///
190 | /// Stop menu item
191 | /// Event arguments
192 | private void Forward_Click(object sender, EventArgs e)
193 | {
194 | App.AudioManager.SkipFiveSecs();
195 | }
196 |
197 | private ApplicationBarIconButton StopPlayButton;
198 | private ApplicationBarIconButton SaveButton;
199 | private ApplicationBarIconButton DeleteButton;
200 | private ApplicationBarIconButton ForwardButton;
201 |
202 | ///
203 | /// Create the application bar with audio controls.
204 | ///
205 | private void CreateAppBar()
206 | {
207 | ApplicationBar appBar = new ApplicationBar();
208 |
209 | StopPlayButton = new ApplicationBarIconButton(new Uri("Assets/Play.png", UriKind.Relative));
210 | StopPlayButton.Click += new EventHandler(StopPlay_Click);
211 | StopPlayButton.Text = "play";
212 | StopPlayButton.IsEnabled = false;
213 | appBar.Buttons.Add(StopPlayButton);
214 |
215 | SaveButton = new ApplicationBarIconButton(new Uri("Assets/Save.png", UriKind.Relative));
216 | SaveButton.Click += new EventHandler(Save_Click);
217 | SaveButton.Text = "stop";
218 | SaveButton.IsEnabled = false;
219 | appBar.Buttons.Add(SaveButton);
220 |
221 | DeleteButton = new ApplicationBarIconButton(new Uri("Assets/Delete.png", UriKind.Relative));
222 | DeleteButton.Click += new EventHandler(Delete_Click);
223 | DeleteButton.Text = "delete";
224 | DeleteButton.IsEnabled = false;
225 | appBar.Buttons.Add(DeleteButton);
226 |
227 | ForwardButton = new ApplicationBarIconButton(new Uri("Assets/Forward.png", UriKind.Relative));
228 | ForwardButton.Click += new EventHandler(Forward_Click);
229 | ForwardButton.Text = "forward";
230 | ForwardButton.IsEnabled = false;
231 | appBar.Buttons.Add(ForwardButton);
232 |
233 | ApplicationBar = appBar;
234 | }
235 | }
236 | }
--------------------------------------------------------------------------------
/AudioRecorder/AudioManager.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | using Microsoft.Xna.Framework;
7 | using Microsoft.Xna.Framework.Audio;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.IO;
11 | using System.IO.IsolatedStorage;
12 | using System.Linq;
13 | using System.Text;
14 | using System.Threading;
15 | using System.Threading.Tasks;
16 | using System.Windows;
17 | using System.Windows.Threading;
18 | using WasapiAudioComp;
19 |
20 | namespace AudioRecorder
21 | {
22 | ///
23 | /// Class handling the audio recording and playback.
24 | ///
25 | public class AudioManager
26 | {
27 | XnaAudio xnaAudio = null;
28 | WasapiAudio wasapiAudio = null;
29 | Boolean wasapiInUse = false;
30 |
31 | ///
32 | /// Constructor.
33 | ///
34 | public AudioManager()
35 | {
36 | // Timer to simulate the XNA Framework game loop (Microphone in
37 | // XnaAudio.cs is from the XNA Framework). Timer is also used to
38 | // monitor the state of audio playback.
39 | DispatcherTimer dt = new DispatcherTimer();
40 | dt.Interval = TimeSpan.FromMilliseconds(33);
41 | dt.Tick += new EventHandler(dt_Tick);
42 | dt.Start();
43 |
44 | xnaAudio = new XnaAudio();
45 | wasapiAudio = new WasapiAudio();
46 | }
47 |
48 | ///
49 | /// Updates the XNA FrameworkDispatcher and updates the playing state
50 | /// if sound has stopped playing.
51 | ///
52 | /// DispatcherTimer.
53 | /// Event arguments.
54 | void dt_Tick(object sender, EventArgs e)
55 | {
56 | try { FrameworkDispatcher.Update(); }
57 | catch { }
58 |
59 | if (true == App.AudioModel.IsPlaying)
60 | {
61 | if (!wasapiInUse)
62 | {
63 | if (!xnaAudio.SoundIsPlaying())
64 | {
65 | xnaAudio.StopPlayback();
66 | App.AudioModel.IsPlaying = false;
67 | }
68 | }
69 | else
70 | {
71 | App.AudioModel.IsPlaying = wasapiAudio.Update();
72 | }
73 | }
74 | else if (true == App.AudioModel.IsRecording && wasapiInUse)
75 | {
76 | // XnaAudio stores the buffer in callback method.
77 | // Buffer is retrieved manually when recording using WASAPI.
78 | byte[] bytes = null;
79 | int size = wasapiAudio.ReadBytes(out bytes);
80 |
81 | if (size > 0)
82 | {
83 | App.AudioModel.AudioBuffer = bytes;
84 | App.AudioModel.stream.Write(bytes, 0, size);
85 | }
86 | }
87 | }
88 |
89 | ///
90 | /// Starts recording.
91 | ///
92 | public void StartRecording()
93 | {
94 | App.AudioModel.IsRecording = true;
95 |
96 | // XNA Microphone records audio in 16-bit, Mono, 16.000 Hz.
97 | if (App.AudioModel.ChannelCount == 1 && App.AudioModel.SampleRate == 16000)
98 | {
99 | wasapiInUse = false;
100 | xnaAudio.StartRecording();
101 | }
102 | // 16-bit, Stereo 44100 Hz is used in WASAPI recording
103 | else
104 | {
105 | wasapiInUse = true;
106 | App.AudioModel.stream.SetLength(0);
107 | wasapiAudio.StartAudioCapture();
108 | }
109 | }
110 |
111 | ///
112 | /// Stops recording.
113 | ///
114 | public void StopRecording()
115 | {
116 | // XNA Microphone records audio in 16-bit, Mono, 16.000 Hz.
117 | if (App.AudioModel.ChannelCount == 1 && App.AudioModel.SampleRate == 16000)
118 | {
119 | xnaAudio.StopRecording();
120 | }
121 | // 16-bit, Stereo 44100 Hz is used in WASAPI recording
122 | else
123 | {
124 | wasapiAudio.StopAudioCapture();
125 | }
126 | saveAudioBuffer();
127 | App.AudioModel.IsRecording = false;
128 | }
129 |
130 | ///
131 | /// Starts playback.
132 | ///
133 | /// Name of the file to play.
134 | /// True to use WASAPI, False to use XNA.
135 | public void StartPlayback(String fileName, Boolean useWasapi)
136 | {
137 | if (!App.AudioModel.IsPlaying)
138 | {
139 | wasapiInUse = useWasapi;
140 | if (!wasapiInUse)
141 | {
142 | xnaAudio.StartPlayback(fileName);
143 | }
144 | else
145 | {
146 | App.AudioModel.LoadAudioBuffer(fileName);
147 | byte[] byteArray = App.AudioModel.stream.ToArray();
148 | if (App.AudioModel.stream.Length > 0)
149 | {
150 | App.AudioModel.stream.Position = 0;
151 | }
152 |
153 | wasapiAudio.SetAudioBytes(byteArray);
154 | wasapiAudio.StartAudioRender();
155 | }
156 | App.AudioModel.IsPlaying = true;
157 | }
158 | }
159 |
160 | ///
161 | /// Stops playback.
162 | ///
163 | public void StopPlayback()
164 | {
165 | if (App.AudioModel.IsPlaying)
166 | {
167 | if (!wasapiInUse)
168 | {
169 | xnaAudio.StopPlayback();
170 | }
171 | else
172 | {
173 | wasapiAudio.StopAudioRender();
174 | }
175 | App.AudioModel.IsPlaying = false;
176 | }
177 | }
178 |
179 | ///
180 | /// Skips the next five seconds of audio playback.
181 | ///
182 | public void SkipFiveSecs()
183 | {
184 | if (App.AudioModel.IsPlaying)
185 | {
186 | if (!wasapiInUse)
187 | {
188 | xnaAudio.SkipFiveSecs();
189 | }
190 | else
191 | {
192 | wasapiAudio.SkipFiveSecs();
193 | }
194 | }
195 | // LOLOLO - What abuot wasapi
196 | }
197 |
198 | ///
199 | /// Saves the audio buffer into isolated storage as a wav-file.
200 | ///
201 | private void saveAudioBuffer()
202 | {
203 | IsolatedStorageFile myStore = IsolatedStorageFile.GetUserStoreForApplication();
204 |
205 | try
206 | {
207 | DateTime dateTime = DateTime.Now;
208 | string fileName = dateTime.ToString("yyyy_MM_dd_HH_mm_ss.wav");
209 | using (var isoFileStream = new IsolatedStorageFileStream(
210 | fileName,
211 | FileMode.OpenOrCreate,
212 | myStore))
213 | {
214 | // Write a header before the actual pcm data
215 | int sampleBits = 16;
216 | int sampleBytes = sampleBits / 8;
217 | int byteRate = App.AudioModel.SampleRate * sampleBytes * App.AudioModel.ChannelCount;
218 | int blockAlign = sampleBytes * App.AudioModel.ChannelCount;
219 | Encoding encoding = Encoding.UTF8;
220 |
221 | isoFileStream.Write(encoding.GetBytes("RIFF"), 0, 4); // "RIFF"
222 | isoFileStream.Write(BitConverter.GetBytes(0), 0, 4); // Chunk Size
223 | isoFileStream.Write(encoding.GetBytes("WAVE"), 0, 4); // Format - "Wave"
224 | isoFileStream.Write(encoding.GetBytes("fmt "), 0, 4); // sub chunk - "fmt"
225 | isoFileStream.Write(BitConverter.GetBytes(16), 0, 4); // sub chunk size
226 | isoFileStream.Write(BitConverter.GetBytes((short)1), 0, 2); // audio format
227 | isoFileStream.Write(BitConverter.GetBytes((short)App.AudioModel.ChannelCount), 0, 2); // num of channels
228 | isoFileStream.Write(BitConverter.GetBytes(App.AudioModel.SampleRate), 0, 4); // sample rate
229 | isoFileStream.Write(BitConverter.GetBytes(byteRate), 0, 4); // byte rate
230 | isoFileStream.Write(BitConverter.GetBytes((short)(blockAlign)), 0, 2); // block align
231 | isoFileStream.Write(BitConverter.GetBytes((short)(sampleBits)), 0, 2); // bits per sample
232 | isoFileStream.Write(encoding.GetBytes("data"), 0, 4); // sub chunk - "data"
233 | isoFileStream.Write(BitConverter.GetBytes(0), 0, 4); // sub chunk size
234 |
235 | // write the actual pcm data
236 | App.AudioModel.stream.Position = 0;
237 | App.AudioModel.stream.CopyTo(isoFileStream);
238 |
239 | // and fill in the blanks
240 | long previousPos = isoFileStream.Position;
241 | isoFileStream.Seek(4, SeekOrigin.Begin);
242 | isoFileStream.Write(BitConverter.GetBytes((int)isoFileStream.Length - 8), 0, 4);
243 | isoFileStream.Seek(40, SeekOrigin.Begin);
244 | isoFileStream.Write(BitConverter.GetBytes((int)isoFileStream.Length - 44), 0, 4);
245 | isoFileStream.Seek(previousPos, SeekOrigin.Begin);
246 |
247 | isoFileStream.Flush();
248 | }
249 | }
250 | catch
251 | {
252 | MessageBox.Show("Error while trying to store audio stream.");
253 | }
254 | }
255 | }
256 | }
257 |
--------------------------------------------------------------------------------
/AudioRecorder/App.xaml.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | using System;
7 | using System.Diagnostics;
8 | using System.Resources;
9 | using System.Windows;
10 | using System.Windows.Markup;
11 | using System.Windows.Navigation;
12 | using Microsoft.Phone.Controls;
13 | using Microsoft.Phone.Shell;
14 | using AudioRecorder.Resources;
15 |
16 | namespace AudioRecorder
17 | {
18 | public partial class App : Application
19 | {
20 | // Members
21 |
22 | ///
23 | /// A static Model used by the views to bind against.
24 | ///
25 | /// The AudioModel object.
26 | private static AudioModel audioModel = null;
27 | public static AudioModel AudioModel
28 | {
29 | get
30 | {
31 | // Delay creation of the view model until necessary
32 | if (audioModel == null)
33 | {
34 | audioModel = new AudioModel();
35 | }
36 |
37 | return audioModel;
38 | }
39 | private set { }
40 | }
41 |
42 | ///
43 | /// A static application wide audio manager.
44 | ///
45 | /// The AudioManager object.
46 | private static AudioManager audioManager = new AudioManager();
47 | public static AudioManager AudioManager
48 | {
49 | get
50 | {
51 | return audioManager;
52 | }
53 | private set { }
54 | }
55 |
56 | ///
57 | /// Provides easy access to the root frame of the Phone Application.
58 | ///
59 | /// The root frame of the Phone Application.
60 | public static PhoneApplicationFrame RootFrame { get; private set; }
61 |
62 | ///
63 | /// Constructor for the Application object.
64 | ///
65 | public App()
66 | {
67 | // Global handler for uncaught exceptions.
68 | UnhandledException += Application_UnhandledException;
69 |
70 | // Standard XAML initialization
71 | InitializeComponent();
72 |
73 | // Phone-specific initialization
74 | InitializePhoneApplication();
75 |
76 | // Language display initialization
77 | InitializeLanguage();
78 |
79 | // Show graphics profiling information while debugging.
80 | if (Debugger.IsAttached)
81 | {
82 | // Display the current frame rate counters.
83 | Application.Current.Host.Settings.EnableFrameRateCounter = true;
84 |
85 | // Show the areas of the app that are being redrawn in each frame.
86 | //Application.Current.Host.Settings.EnableRedrawRegions = true;
87 |
88 | // Enable non-production analysis visualization mode,
89 | // which shows areas of a page that are handed off to GPU with a colored overlay.
90 | //Application.Current.Host.Settings.EnableCacheVisualization = true;
91 |
92 | // Prevent the screen from turning off while under the debugger by disabling
93 | // the application's idle detection.
94 | // Caution:- Use this under debug mode only. Application that disables user idle detection will continue to run
95 | // and consume battery power when the user is not using the phone.
96 | PhoneApplicationService.Current.UserIdleDetectionMode = IdleDetectionMode.Disabled;
97 | }
98 |
99 | }
100 |
101 | // Code to execute when the application is launching (eg, from Start)
102 | // This code will not execute when the application is reactivated
103 | private void Application_Launching(object sender, LaunchingEventArgs e)
104 | {
105 | }
106 |
107 | // Code to execute when the application is activated (brought to foreground)
108 | // This code will not execute when the application is first launched
109 | private void Application_Activated(object sender, ActivatedEventArgs e)
110 | {
111 | }
112 |
113 | // Code to execute when the application is deactivated (sent to background)
114 | // This code will not execute when the application is closing
115 | private void Application_Deactivated(object sender, DeactivatedEventArgs e)
116 | {
117 | }
118 |
119 | // Code to execute when the application is closing (eg, user hit Back)
120 | // This code will not execute when the application is deactivated
121 | private void Application_Closing(object sender, ClosingEventArgs e)
122 | {
123 | }
124 |
125 | // Code to execute if a navigation fails
126 | private void RootFrame_NavigationFailed(object sender, NavigationFailedEventArgs e)
127 | {
128 | if (Debugger.IsAttached)
129 | {
130 | // A navigation has failed; break into the debugger
131 | Debugger.Break();
132 | }
133 | }
134 |
135 | // Code to execute on Unhandled Exceptions
136 | private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
137 | {
138 | if (Debugger.IsAttached)
139 | {
140 | // An unhandled exception has occurred; break into the debugger
141 | Debugger.Break();
142 | }
143 | }
144 |
145 | #region Phone application initialization
146 |
147 | // Avoid double-initialization
148 | private bool phoneApplicationInitialized = false;
149 |
150 | // Do not add any additional code to this method
151 | private void InitializePhoneApplication()
152 | {
153 | if (phoneApplicationInitialized)
154 | return;
155 |
156 | // Create the frame but don't set it as RootVisual yet; this allows the splash
157 | // screen to remain active until the application is ready to render.
158 | RootFrame = new PhoneApplicationFrame();
159 | RootFrame.Navigated += CompleteInitializePhoneApplication;
160 |
161 | // Handle navigation failures
162 | RootFrame.NavigationFailed += RootFrame_NavigationFailed;
163 |
164 | // Handle reset requests for clearing the backstack
165 | RootFrame.Navigated += CheckForResetNavigation;
166 |
167 | // Ensure we don't initialize again
168 | phoneApplicationInitialized = true;
169 | }
170 |
171 | // Do not add any additional code to this method
172 | private void CompleteInitializePhoneApplication(object sender, NavigationEventArgs e)
173 | {
174 | // Set the root visual to allow the application to render
175 | if (RootVisual != RootFrame)
176 | RootVisual = RootFrame;
177 |
178 | // Remove this handler since it is no longer needed
179 | RootFrame.Navigated -= CompleteInitializePhoneApplication;
180 | }
181 |
182 | private void CheckForResetNavigation(object sender, NavigationEventArgs e)
183 | {
184 | // If the app has received a 'reset' navigation, then we need to check
185 | // on the next navigation to see if the page stack should be reset
186 | if (e.NavigationMode == NavigationMode.Reset)
187 | RootFrame.Navigated += ClearBackStackAfterReset;
188 | }
189 |
190 | private void ClearBackStackAfterReset(object sender, NavigationEventArgs e)
191 | {
192 | // Unregister the event so it doesn't get called again
193 | RootFrame.Navigated -= ClearBackStackAfterReset;
194 |
195 | // Only clear the stack for 'new' (forward) and 'refresh' navigations
196 | if (e.NavigationMode != NavigationMode.New && e.NavigationMode != NavigationMode.Refresh)
197 | return;
198 |
199 | // For UI consistency, clear the entire page stack
200 | while (RootFrame.RemoveBackEntry() != null)
201 | {
202 | ; // do nothing
203 | }
204 | }
205 |
206 | #endregion
207 |
208 | // Initialize the app's font and flow direction as defined in its localized resource strings.
209 | //
210 | // To ensure that the font of your application is aligned with its supported languages and that the
211 | // FlowDirection for each of those languages follows its traditional direction, ResourceLanguage
212 | // and ResourceFlowDirection should be initialized in each resx file to match these values with that
213 | // file's culture. For example:
214 | //
215 | // AppResources.es-ES.resx
216 | // ResourceLanguage's value should be "es-ES"
217 | // ResourceFlowDirection's value should be "LeftToRight"
218 | //
219 | // AppResources.ar-SA.resx
220 | // ResourceLanguage's value should be "ar-SA"
221 | // ResourceFlowDirection's value should be "RightToLeft"
222 | //
223 | // For more info on localizing Windows Phone apps see http://go.microsoft.com/fwlink/?LinkId=262072.
224 | //
225 | private void InitializeLanguage()
226 | {
227 | try
228 | {
229 | // Set the font to match the display language defined by the
230 | // ResourceLanguage resource string for each supported language.
231 | //
232 | // Fall back to the font of the neutral language if the Display
233 | // language of the phone is not supported.
234 | //
235 | // If a compiler error is hit then ResourceLanguage is missing from
236 | // the resource file.
237 | RootFrame.Language = XmlLanguage.GetLanguage(AppResources.ResourceLanguage);
238 |
239 | // Set the FlowDirection of all elements under the root frame based
240 | // on the ResourceFlowDirection resource string for each
241 | // supported language.
242 | //
243 | // If a compiler error is hit then ResourceFlowDirection is missing from
244 | // the resource file.
245 | FlowDirection flow = (FlowDirection)Enum.Parse(typeof(FlowDirection), AppResources.ResourceFlowDirection);
246 | RootFrame.FlowDirection = flow;
247 | }
248 | catch
249 | {
250 | // If an exception is caught here it is most likely due to either
251 | // ResourceLangauge not being correctly set to a supported language
252 | // code or ResourceFlowDirection is set to a value other than LeftToRight
253 | // or RightToLeft.
254 |
255 | if (Debugger.IsAttached)
256 | {
257 | Debugger.Break();
258 | }
259 |
260 | throw;
261 | }
262 | }
263 | }
264 | }
--------------------------------------------------------------------------------
/WasapiAudioComp/WasapiAudio.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | #include "pch.h"
7 | #include "WasapiAudio.h"
8 |
9 | using namespace WasapiAudioComp;
10 | using namespace Platform;
11 | using namespace Windows::System::Threading;
12 |
13 | #define REFTIMES_PER_SEC 10000000
14 | #define REFTIMES_PER_MILLISEC 10000
15 |
16 | #define MY_MAX_RAW_BUFFER_SIZE 1024*128
17 |
18 | /**
19 | * Helper function fill WAVEFORMATEX struct.
20 | * @param format WAVEFORMATEX struct to fill,
21 | * @param channels The number of channels in pcm data.
22 | * @param sampleRate The sample rate in pcm data.
23 | * @param bits The bit rate in pcm data.
24 | */
25 | void MyFillPcmFormat(WAVEFORMATEX& format, WORD channels, int sampleRate, WORD bits)
26 | {
27 | format.wFormatTag = WAVE_FORMAT_PCM;
28 | format.nChannels = channels;
29 | format.nSamplesPerSec = sampleRate;
30 | format.wBitsPerSample = bits;
31 | format.nBlockAlign = format.nChannels * (format.wBitsPerSample / 8);
32 | format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
33 | format.cbSize = 0;
34 | }
35 |
36 | /**
37 | * @class WasapiAudio
38 | * @brief Class to capture and render audio through WASAPI.
39 | */
40 |
41 | /**
42 | * Constructor.
43 | */
44 | WasapiAudio::WasapiAudio() :
45 | m_waveFormatEx(NULL),
46 | m_pDefaultCaptureDevice(NULL),
47 | m_pDefaultRenderDevice(NULL),
48 | m_pCaptureClient(NULL),
49 | m_pRenderClient(NULL),
50 | m_sourceFrameSizeInBytes(0),
51 | started(false),
52 | audioBytes(NULL)
53 | {
54 | }
55 |
56 | /**
57 | * Destructor.
58 | */
59 | WasapiAudio::~WasapiAudio()
60 | {
61 | }
62 |
63 | /**
64 | * Start audio capturing using WASAPI.
65 | * @return The success of the operation.
66 | */
67 | bool WasapiAudio::StartAudioCapture()
68 | {
69 | bool ret = false;
70 |
71 | if (!started)
72 | {
73 | HRESULT hr = InitCapture();
74 | if (SUCCEEDED(hr))
75 | {
76 | ret = started = true;
77 | }
78 | }
79 |
80 | return ret;
81 | }
82 |
83 | /**
84 | * Stop audio capturing using WASAPI.
85 | * @return The success of the operation.
86 | */
87 | bool WasapiAudio::StopAudioCapture()
88 | {
89 | bool ret = false;
90 |
91 | if (started)
92 | {
93 | HRESULT hr = S_OK;
94 |
95 | if (m_pDefaultCaptureDevice)
96 | {
97 | hr = m_pDefaultCaptureDevice->Stop();
98 | }
99 |
100 | if (m_pCaptureClient)
101 | {
102 | m_pCaptureClient->Release();
103 | m_pCaptureClient = NULL;
104 | }
105 |
106 | if (m_pDefaultCaptureDevice)
107 | {
108 | m_pDefaultCaptureDevice->Release();
109 | m_pDefaultCaptureDevice = NULL;
110 | }
111 |
112 | if (m_waveFormatEx)
113 | {
114 | CoTaskMemFree((LPVOID)m_waveFormatEx);
115 | m_waveFormatEx = NULL;
116 | }
117 |
118 | if (SUCCEEDED(hr))
119 | {
120 | started = false;
121 | ret = true;
122 | }
123 | }
124 |
125 | return ret;
126 | }
127 |
128 | /**
129 | * Read accumulated audio data.
130 | * @param byteArray The byte array to be filled with audio data.
131 | * @return The number of audio bytes returned.
132 | */
133 | int WasapiAudio::ReadBytes(Platform::Array^* byteArray)
134 | {
135 | int ret = 0;
136 | if (!started) return ret;
137 |
138 | BYTE *tempBuffer = new BYTE[MY_MAX_RAW_BUFFER_SIZE];
139 | UINT32 packetSize = 0;
140 | HRESULT hr = S_OK;
141 | long accumulatedBytes = 0;
142 |
143 | if (tempBuffer)
144 | {
145 | hr = m_pCaptureClient->GetNextPacketSize(&packetSize);
146 |
147 | while (SUCCEEDED(hr)
148 | && packetSize > 0
149 | && MY_MAX_RAW_BUFFER_SIZE > accumulatedBytes + packetSize * m_sourceFrameSizeInBytes)
150 | {
151 | BYTE* packetData = nullptr;
152 | UINT32 frameCount = 0;
153 | DWORD flags = 0;
154 | if (SUCCEEDED(hr))
155 | {
156 | hr = m_pCaptureClient->GetBuffer(&packetData, &frameCount, &flags, nullptr, nullptr);
157 | unsigned int incomingBufferSize = frameCount * m_sourceFrameSizeInBytes;
158 |
159 | memcpy(tempBuffer + accumulatedBytes, packetData, incomingBufferSize);
160 | accumulatedBytes += incomingBufferSize;
161 | }
162 |
163 | if (SUCCEEDED(hr))
164 | {
165 | hr = m_pCaptureClient->ReleaseBuffer(frameCount);
166 | }
167 |
168 | if (SUCCEEDED(hr))
169 | {
170 | hr = m_pCaptureClient->GetNextPacketSize(&packetSize);
171 | }
172 | }
173 |
174 | // Copy the available capture data to the array.
175 | auto temp = ref new Platform::Array(accumulatedBytes);
176 | for(long i = 0; i < accumulatedBytes; i++)
177 | {
178 | temp[i] = tempBuffer[i];
179 | }
180 | *byteArray = temp;
181 | ret = accumulatedBytes;
182 |
183 | // Reset byte counter
184 | accumulatedBytes = 0;
185 | }
186 |
187 | delete[] tempBuffer;
188 |
189 | return ret;
190 | }
191 |
192 | /**
193 | * Start audio rendering using WASAPI.
194 | * @return The success of the operation.
195 | */
196 | bool WasapiAudio::StartAudioRender()
197 | {
198 | bool ret = false;
199 |
200 | if (!started)
201 | {
202 | HRESULT hr = InitRender();
203 | if (SUCCEEDED(hr))
204 | {
205 | ret = started = true;
206 | }
207 | }
208 |
209 | return ret;
210 | }
211 |
212 | /**
213 | * Stop audio rendering using WASAPI.
214 | * @return The success of the operation.
215 | */
216 | bool WasapiAudio::StopAudioRender()
217 | {
218 | bool ret = false;
219 |
220 | if (started)
221 | {
222 | HRESULT hr = S_OK;
223 |
224 | if (m_pDefaultRenderDevice)
225 | {
226 | hr = m_pDefaultRenderDevice->Stop();
227 | }
228 |
229 | if (m_pRenderClient)
230 | {
231 | m_pRenderClient->Release();
232 | m_pRenderClient = NULL;
233 | }
234 |
235 | if (m_pDefaultRenderDevice)
236 | {
237 | m_pDefaultRenderDevice->Release();
238 | m_pDefaultRenderDevice = NULL;
239 | }
240 |
241 | if (m_waveFormatEx)
242 | {
243 | CoTaskMemFree((LPVOID)m_waveFormatEx);
244 | m_waveFormatEx = NULL;
245 | }
246 |
247 | if (SUCCEEDED(hr))
248 | {
249 | started = false;
250 | ret = true;
251 | }
252 | }
253 |
254 | return ret;
255 | }
256 |
257 | /**
258 | * Set audio data to be rendered.
259 | * @param byteArray The byte array to be rendered.
260 | */
261 | void WasapiAudio::SetAudioBytes(const Platform::Array^ byteArray)
262 | {
263 | delete audioBytes;
264 |
265 | // no need for the wav-header
266 | audioBytes = new BYTE[byteArray->Length-44];
267 | int availableBytes = byteArray->Length-44;
268 |
269 | for(long i = 0; i < availableBytes; i++)
270 | {
271 | audioBytes[i] = byteArray[i+44];
272 | }
273 |
274 | audioIndex = 0;
275 | audioByteCount = availableBytes;
276 | }
277 |
278 | /**
279 | * Feeds the render device with audio data.
280 | * @return True if there is audio data be rendered.
281 | */
282 | bool WasapiAudio::Update()
283 | {
284 | bool ret = false;
285 | if (!started) return ret;
286 |
287 | HRESULT hr = S_OK;
288 | UINT32 bufferFrameCount;
289 | UINT32 numFramesPadding;
290 | UINT32 numFramesAvailable;
291 | BYTE *pData = NULL;
292 | DWORD flags = 0;
293 |
294 | // Get the actual size of the allocated buffer.
295 | hr = m_pDefaultRenderDevice->GetBufferSize(&bufferFrameCount);
296 |
297 | // See how much buffer space is available.
298 | if (SUCCEEDED(hr))
299 | {
300 | hr = m_pDefaultRenderDevice->GetCurrentPadding(&numFramesPadding);
301 | numFramesAvailable = bufferFrameCount - numFramesPadding;
302 | }
303 |
304 | if (SUCCEEDED(hr))
305 | {
306 | // Grab all the available space in the shared buffer.
307 | hr = m_pRenderClient->GetBuffer(numFramesAvailable, &pData);
308 | }
309 |
310 | if (SUCCEEDED(hr))
311 | {
312 | UINT32 numFramesInData = (audioByteCount - audioIndex) / m_sourceFrameSizeInBytes;
313 | if (numFramesInData > numFramesAvailable)
314 | {
315 | numFramesInData = numFramesAvailable;
316 | }
317 |
318 | if (numFramesInData > 0)
319 | {
320 | memcpy(pData, audioBytes+audioIndex, numFramesInData * m_sourceFrameSizeInBytes);
321 | audioIndex += numFramesInData * m_sourceFrameSizeInBytes;
322 | hr = m_pRenderClient->ReleaseBuffer(numFramesInData, 0);
323 | }
324 | else
325 | {
326 | hr = m_pRenderClient->ReleaseBuffer(0, AUDCLNT_BUFFERFLAGS_SILENT);
327 | }
328 | }
329 |
330 | if (SUCCEEDED(hr))
331 | {
332 | hr = m_pDefaultRenderDevice->GetCurrentPadding(&numFramesPadding);
333 | }
334 |
335 | if (SUCCEEDED(hr) && numFramesPadding > 0)
336 | {
337 | ret = true;
338 | }
339 |
340 | if (ret == false)
341 | {
342 | // Nothing to play anymore
343 | started = false;
344 | }
345 |
346 | return ret;
347 | }
348 |
349 | /**
350 | * Skips the next five seconds of audio.
351 | */
352 | void WasapiAudio::SkipFiveSecs()
353 | {
354 | int newAudioIndex = audioIndex + 2 * 2 * 44100 * 5;
355 | if (newAudioIndex < audioByteCount)
356 | {
357 | audioIndex = newAudioIndex;
358 | }
359 | }
360 |
361 | /**
362 | * Initialize WASAPI audio capture device.
363 | * @return The success of the operation.
364 | */
365 | HRESULT WasapiAudio::InitCapture()
366 | {
367 | HRESULT hr = E_FAIL;
368 |
369 | LPCWSTR captureId = GetDefaultAudioCaptureId(AudioDeviceRole::Default);
370 |
371 | if (NULL == captureId)
372 | {
373 | hr = E_FAIL;
374 | }
375 | else
376 | {
377 | hr = ActivateAudioInterface(captureId, __uuidof(IAudioClient2), (void**)&m_pDefaultCaptureDevice);
378 | }
379 |
380 | if (SUCCEEDED(hr))
381 | {
382 | hr = m_pDefaultCaptureDevice->GetMixFormat(&m_waveFormatEx);
383 | }
384 |
385 | // Set the category through SetClientProperties
386 | AudioClientProperties properties = {};
387 | if (SUCCEEDED(hr))
388 | {
389 | properties.cbSize = sizeof AudioClientProperties;
390 | properties.eCategory = AudioCategory_Other;
391 | // Note that AudioCategory_Other is the only valid category for capture and loopback streams.
392 | // From: http://msdn.microsoft.com/en-us/library/windows/desktop/hh404178(v=vs.85).aspx
393 | hr = m_pDefaultCaptureDevice->SetClientProperties(&properties);
394 | }
395 |
396 | if (SUCCEEDED(hr))
397 | {
398 | WAVEFORMATEX temp;
399 | MyFillPcmFormat(temp, 2, 44100, 16); // stereo, 44100 Hz, 16 bit
400 |
401 | *m_waveFormatEx = temp;
402 | m_sourceFrameSizeInBytes = (m_waveFormatEx->wBitsPerSample / 8) * m_waveFormatEx->nChannels;
403 |
404 | // using device to capture stereo requires the flag 0x8800000, or at least some part of it
405 | hr = m_pDefaultCaptureDevice->Initialize(AUDCLNT_SHAREMODE_SHARED, 0x88000000, 1000 * 10000, 0, m_waveFormatEx, NULL);
406 | }
407 |
408 | if (SUCCEEDED(hr))
409 | {
410 | hr = m_pDefaultCaptureDevice->GetService(__uuidof(IAudioCaptureClient), (void**)&m_pCaptureClient);
411 | }
412 |
413 | if (SUCCEEDED(hr))
414 | {
415 | hr = m_pDefaultCaptureDevice->Start();
416 | }
417 |
418 | if (captureId)
419 | {
420 | CoTaskMemFree((LPVOID)captureId);
421 | }
422 |
423 | return hr;
424 | }
425 |
426 | /**
427 | * Initialize WASAPI audio render device.
428 | * @return The success of the operation.
429 | */
430 | HRESULT WasapiAudio::InitRender()
431 | {
432 | HRESULT hr = E_FAIL;
433 |
434 | LPCWSTR renderId = GetDefaultAudioRenderId(AudioDeviceRole::Default);
435 |
436 | if (NULL == renderId)
437 | {
438 | hr = E_FAIL;
439 | }
440 | else
441 | {
442 | hr = ActivateAudioInterface(renderId, __uuidof(IAudioClient2), (void**)&m_pDefaultRenderDevice);
443 | }
444 |
445 | if (SUCCEEDED(hr))
446 | {
447 | hr = m_pDefaultRenderDevice->GetMixFormat(&m_waveFormatEx);
448 | }
449 |
450 | // Set the category through SetClientProperties
451 | AudioClientProperties properties = {};
452 | if (SUCCEEDED(hr))
453 | {
454 | properties.cbSize = sizeof AudioClientProperties;
455 | properties.eCategory = AudioCategory_Other;
456 | hr = m_pDefaultRenderDevice->SetClientProperties(&properties);
457 | }
458 |
459 | if (SUCCEEDED(hr))
460 | {
461 | WAVEFORMATEX temp;
462 | MyFillPcmFormat(temp, 2, 44100, 16); // stereo, 44100 Hz, 16 bit
463 |
464 | *m_waveFormatEx = temp;
465 | m_sourceFrameSizeInBytes = (m_waveFormatEx->wBitsPerSample / 8) * m_waveFormatEx->nChannels;
466 |
467 | hr = m_pDefaultRenderDevice->Initialize(AUDCLNT_SHAREMODE_SHARED, 0x88000000, 1000 * 10000, 0, m_waveFormatEx, NULL);
468 | }
469 |
470 | if (SUCCEEDED(hr))
471 | {
472 | hr = m_pDefaultRenderDevice->GetService(__uuidof(IAudioRenderClient), (void**)&m_pRenderClient);
473 | }
474 |
475 | if (SUCCEEDED(hr))
476 | {
477 | hr = m_pDefaultRenderDevice->Start();
478 | }
479 |
480 | if (renderId)
481 | {
482 | CoTaskMemFree((LPVOID)renderId);
483 | }
484 |
485 | return hr;
486 | }
--------------------------------------------------------------------------------
/AudioRecorder/MainPage.xaml.cs:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2013-2014 Microsoft Mobile. All rights reserved.
3 | * See the license text file delivered with this project for more information.
4 | */
5 |
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Net;
10 | using System.Windows;
11 | using System.Windows.Controls;
12 | using System.Windows.Navigation;
13 | using Microsoft.Phone.Controls;
14 | using Microsoft.Phone.Shell;
15 | using AudioRecorder.Resources;
16 | using System.Windows.Media.Imaging;
17 | using System.Windows.Media;
18 |
19 | namespace AudioRecorder
20 | {
21 | ///
22 | /// Main page of the application - The Recorder.
23 | ///
24 | public partial class MainPage : PhoneApplicationPage
25 | {
26 | // Members
27 | private Boolean updateVisualization = false;
28 | private enum VisualizationStyle { Value, Deteriorating, Time };
29 | private VisualizationStyle visualizationStyle = VisualizationStyle.Time;
30 |
31 | private int barCount = 11;
32 | private Image[] barImages;
33 | private int barBottomMargin;
34 |
35 | ///
36 | /// Constructor.
37 | ///
38 | public MainPage()
39 | {
40 | InitializeComponent();
41 |
42 | if (App.Current.Host.Content.ScaleFactor == 150)
43 | {
44 | BgBrush.ImageSource = new BitmapImage(
45 | new Uri(@"Assets/Background-720p.png", UriKind.Relative)
46 | );
47 | barBottomMargin = 444;
48 | }
49 | else
50 | {
51 | BgBrush.ImageSource = new BitmapImage(
52 | new Uri(@"Assets/Background.png", UriKind.Relative)
53 | );
54 | barBottomMargin = 394;
55 | }
56 |
57 | SystemTray.SetOpacity(this, 0.01);
58 |
59 | App.AudioModel.AudioBufferChanged += new AudioBufferChangedEventHandler(AudioBufferChanged);
60 |
61 | InitializeVisualizationBars();
62 | }
63 |
64 | ///
65 | /// Initialize visualization bars. Position changes with aspect ratio.
66 | ///
67 | protected void InitializeVisualizationBars()
68 | {
69 | // Initialize visualization bars
70 | barImages = new Image[barCount];
71 | for (int i = 0; i < barCount; i++)
72 | {
73 | Image bar = new Image();
74 | BitmapImage barImage = new BitmapImage(new Uri("Assets/FullBar.png", UriKind.Relative));
75 | bar.Source = barImage;
76 | bar.Stretch = Stretch.None;
77 | bar.HorizontalAlignment = HorizontalAlignment.Left;
78 | bar.VerticalAlignment = VerticalAlignment.Bottom;
79 | bar.Margin = new Thickness(36 + ((35 + 36) * i) / 2, 0, 0, barBottomMargin);
80 | bar.Height = 0;
81 | barImages[i] = bar;
82 | ContentPanel.Children.Add(bar);
83 | }
84 | }
85 |
86 | ///
87 | /// Start/Stop recording. Updates the button graphics.
88 | ///
89 | /// RecButton.
90 | /// Event arguments.
91 | private void OnRecButtonTap(object sender, System.Windows.Input.GestureEventArgs e)
92 | {
93 | if (App.AudioModel.IsRecording)
94 | {
95 | App.AudioManager.StopRecording();
96 | BitmapImage recImage = new BitmapImage(new Uri("Assets/RecButtonUp.png", UriKind.Relative));
97 | RecButton.Source = recImage;
98 | BitmapImage lightImage = new BitmapImage(new Uri("Assets/RecLightOff.png", UriKind.Relative));
99 | RecLight.Source = lightImage;
100 | updateVisualization = false;
101 | for (int i = 0; i < barImages.Length; i++)
102 | {
103 | barImages[i].Height = 0;
104 | }
105 | }
106 | else
107 | {
108 | updateVisualization = true;
109 | App.AudioManager.StartRecording();
110 | BitmapImage recImage = new BitmapImage(new Uri("Assets/RecButtonDown.png", UriKind.Relative));
111 | RecButton.Source = recImage;
112 | BitmapImage lightImage = new BitmapImage(new Uri("Assets/RecLightOn.png", UriKind.Relative));
113 | RecLight.Source = lightImage;
114 | }
115 | }
116 |
117 | ///
118 | /// Change recording quality.
119 | ///
120 | /// Quality stack panel.
121 | /// Event arguments.
122 | private void OnQualityTap(object sender, System.Windows.Input.GestureEventArgs e)
123 | {
124 | if (!App.AudioModel.IsRecording)
125 | {
126 | if (MonoStereoText.Text == "Mono")
127 | {
128 | MonoStereoText.Text = "Stereo";
129 | SampleRateText.Text = "44100 Hz";
130 | App.AudioModel.ChannelCount = 2;
131 | App.AudioModel.SampleRate = 44100;
132 | }
133 | else
134 | {
135 | MonoStereoText.Text = "Mono";
136 | SampleRateText.Text = "16000 Hz";
137 | App.AudioModel.ChannelCount = 1;
138 | App.AudioModel.SampleRate = 16000;
139 | }
140 | }
141 | }
142 |
143 | ///
144 | /// Change visualization.
145 | ///
146 | /// VisualizationRect.
147 | /// Event arguments.
148 | private void OnVisualizationTap(object sender, System.Windows.Input.GestureEventArgs e)
149 | {
150 | if (visualizationStyle == VisualizationStyle.Value)
151 | {
152 | visualizationStyle = VisualizationStyle.Deteriorating;
153 | }
154 | else if (visualizationStyle == VisualizationStyle.Deteriorating)
155 | {
156 | visualizationStyle = VisualizationStyle.Time;
157 | }
158 | else if (visualizationStyle == VisualizationStyle.Time)
159 | {
160 | visualizationStyle = VisualizationStyle.Value;
161 | }
162 | }
163 |
164 | ///
165 | /// Navigate to AudioFilePage.
166 | ///
167 | /// FilesButton.
168 | /// Event arguments.
169 | private void OnFilesButtonTap(object sender, System.Windows.Input.GestureEventArgs e)
170 | {
171 | if (!App.AudioModel.IsRecording)
172 | {
173 | NavigationService.Navigate(new Uri("/AudioFilePage.xaml", UriKind.Relative));
174 | }
175 | }
176 |
177 | ///
178 | /// Change files button graphics when button is pressed.
179 | ///
180 | /// FilesButton.
181 | /// Event arguments.
182 | private void OnFilesButtonEnter(object sender, System.Windows.Input.MouseEventArgs e)
183 | {
184 | BitmapImage image = new BitmapImage(new Uri("Assets/FilesButtonDown.png", UriKind.Relative));
185 | FilesButton.Source = image;
186 | }
187 |
188 | ///
189 | /// Change files button graphics when button is unpressed.
190 | ///
191 | /// FilesButton.
192 | /// Event arguments.
193 | private void OnFilesButtonLeave(object sender, System.Windows.Input.MouseEventArgs e)
194 | {
195 | BitmapImage image = new BitmapImage(new Uri("Assets/FilesButtonUp.png", UriKind.Relative));
196 | FilesButton.Source = image;
197 | }
198 |
199 | ///
200 | /// Update visualization whenever the audio buffer changes.
201 | ///
202 | /// AudioModel.
203 | /// Event arguments.
204 | private void AudioBufferChanged(object sender, EventArgs e)
205 | {
206 | UpdateVisualization(App.AudioModel.AudioBuffer, 0, App.AudioModel.AudioBuffer.Length);
207 | }
208 |
209 | ///
210 | /// Call correct visualization function.
211 | ///
212 | /// Audio data buffer.
213 | /// Position of buffer from where to visualize.
214 | /// Sample size to visualize.
215 | private void UpdateVisualization(byte[] array, int position, int sampleSize)
216 | {
217 | if (updateVisualization == false) return;
218 |
219 | if (visualizationStyle == VisualizationStyle.Value)
220 | {
221 | VisualizeValue(array, position, sampleSize);
222 | }
223 | else if (visualizationStyle == VisualizationStyle.Deteriorating)
224 | {
225 | VisualizeDeterioratingValue(array, position, sampleSize);
226 | }
227 | else
228 | {
229 | VisualizeValueByTime(array, position, sampleSize);
230 | }
231 | }
232 |
233 | ///
234 | /// Helper function to get the highest value in the audio buffer.
235 | ///
236 | /// Audio data buffer.
237 | /// Position of buffer from where to visualize.
238 | /// Sample size to visualize.
239 | private long GetHighestValue(byte[] array, int position, int sampleSize)
240 | {
241 | long highest = 0;
242 | int end = position + sampleSize;
243 | for (int i = position; i < end; i++)
244 | {
245 | long value = BitConverter.ToInt16(array, i);
246 |
247 | if (value < 0)
248 | {
249 | value *= -1;
250 | }
251 |
252 | if (highest < value)
253 | {
254 | highest = value;
255 | }
256 | i++;
257 | }
258 | return highest;
259 | }
260 |
261 | ///
262 | /// Update the visualization bars to show value.
263 | ///
264 | /// Audio data buffer.
265 | /// Position of buffer from where to visualize.
266 | /// Sample size to visualize.
267 | private void VisualizeValue(byte[] array, int position, int sampleSize)
268 | {
269 | long highest = GetHighestValue(array, position, sampleSize);
270 |
271 | // Maximum value of a 16-bit signed integer.
272 | const double MaxValue = 32768f;
273 |
274 | int barsVisible = (int)(highest / MaxValue * 16 + 0.5);
275 | double barHeight = 15 * barsVisible; // full bar image height = 248 pixels = 16 bars
276 |
277 | if (barHeight < barImages[barCount / 2].Height / 2)
278 | {
279 | barsVisible = (int)((barImages[barCount / 2].Height) / 15);
280 | barImages[barCount / 2].Height = barsVisible / 2 * 15;
281 | }
282 | else
283 | {
284 | barImages[barCount / 2].Height = barHeight;
285 | }
286 |
287 | for (int i = barCount / 2 - 1; i >= 0; i--)
288 | {
289 | barsVisible /= 2;
290 | double tempHeight = barsVisible * 15;
291 | if (tempHeight < 0) tempHeight = 0;
292 | barImages[i].Height = barImages[barCount - 1 - i].Height = tempHeight;
293 | }
294 | }
295 |
296 | ///
297 | /// Update the visualization bars to show deteriorating value.
298 | ///
299 | /// Audio data buffer.
300 | /// Position of buffer from where to visualize.
301 | /// Sample size to visualize.
302 | private void VisualizeDeterioratingValue(byte[] array, int position, int sampleSize)
303 | {
304 | long highest = GetHighestValue(array, position, sampleSize);
305 |
306 | // Maximum value of a 16-bit signed integer.
307 | const double MaxValue = 32768f;
308 |
309 | int barsVisible = (int)(highest / MaxValue * 16 + 0.5);
310 | double barHeight = 15 * barsVisible; // full bar image height = 248 pixels = 16 bars
311 |
312 | for (int i = 0; i < barCount / 2; i++)
313 | {
314 | int tempBarsVisible = (int)(barImages[i + 1].Height / 15 / 2);
315 | barImages[i].Height = barImages[barCount - 1 - i].Height = tempBarsVisible * 15; //barImages[i + 1].Height / 2;
316 | }
317 | barImages[barCount / 2].Height = barHeight;
318 | }
319 |
320 | ///
321 | /// Update the visualization bars to show value by time.
322 | ///
323 | /// Audio data buffer.
324 | /// Position of buffer from where to visualize.
325 | /// Sample size to visualize.
326 | private void VisualizeValueByTime(byte[] array, int position, int sampleSize)
327 | {
328 | long highest = GetHighestValue(array, position, sampleSize);
329 |
330 | // Maximum value of a 16-bit signed integer.
331 | const double MaxValue = 32768f;
332 |
333 | int barsVisible = (int)(highest / MaxValue * 16 + 0.5);
334 | double barHeight = 15 * barsVisible; // full bar image height = 248 pixels = 16 bars
335 |
336 | for (int i = 0; i < barCount - 1; i++)
337 | {
338 | barImages[i].Height = barImages[i + 1].Height;
339 | }
340 | barImages[barCount - 1].Height = barHeight;
341 | }
342 | }
343 | }
--------------------------------------------------------------------------------