├── 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 | ![Main page](/doc/audio_recorder_main_page_small.png?raw=true)  9 | ![Audio files page](/doc/audio_recorder_files_page_small.png?raw=true) 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 | } --------------------------------------------------------------------------------