diff --git a/Telegram/PrepareWin.bat b/Telegram/PrepareWin.bat
index 16488a81c..1c46b3295 100644
--- a/Telegram/PrepareWin.bat
+++ b/Telegram/PrepareWin.bat
@@ -1,10 +1,10 @@
 @echo OFF
 
-set "AppVersion=8017"
-set "AppVersionStrSmall=0.8.17"
-set "AppVersionStr=0.8.17"
-set "AppVersionStrFull=0.8.17.0"
-set "DevChannel=0"
+set "AppVersion=8018"
+set "AppVersionStrSmall=0.8.18"
+set "AppVersionStr=0.8.18"
+set "AppVersionStrFull=0.8.18.0"
+set "DevChannel=1"
 
 if %DevChannel% neq 0 goto preparedev
 
diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp
index 692476419..137ece6ba 100644
--- a/Telegram/SourceFiles/app.cpp
+++ b/Telegram/SourceFiles/app.cpp
@@ -1543,6 +1543,7 @@ namespace App {
 		cSetStickerSets(StickerSets());
 		cSetStickerSetsOrder(StickerSetsOrder());
 		cSetLastStickersUpdate(0);
+		LOG(("Stickers: cleared everything!"));
 		::videoItems.clear();
 		::audioItems.clear();
 		::documentItems.clear();
diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp
index 04abaf881..dd6c37317 100644
--- a/Telegram/SourceFiles/audio.cpp
+++ b/Telegram/SourceFiles/audio.cpp
@@ -96,6 +96,7 @@ bool _checkALError() {
 void audioInit() {
 	if (!capture) {
 		capture = new AudioCapture();
+		cSetHasAudioCapture(capture->check());
 	}
 
 	uint64 ms = getms();
@@ -202,6 +203,7 @@ void audioInit() {
 	avcodec_register_all();
 
 	LOG(("Audio init time: %1").arg(getms() - ms));
+	cSetHasAudioPlayer(true);
 }
 
 void audioPlayNotify() {
@@ -240,6 +242,9 @@ void audioFinish() {
 		alcCloseDevice(audioDevice);
 		audioDevice = 0;
 	}
+
+	cSetHasAudioCapture(false);
+	cSetHasAudioPlayer(false);
 }
 
 AudioPlayer::AudioPlayer() : _current(0),
@@ -421,6 +426,16 @@ void AudioCapture::stop(bool needResult) {
 	emit captureOnStop(needResult);
 }
 
+bool AudioCapture::check() {
+	if (const ALCchar *def = alcGetString(0, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)) {
+		if (ALCdevice *dev = alcCaptureOpenDevice(def, AudioVoiceMsgFrequency, AL_FORMAT_MONO16, AudioVoiceMsgFrequency / 5)) {
+			alcCaptureCloseDevice(dev);
+			return _checkALCError();
+		}
+	}
+	return false;
+}
+
 AudioCapture::~AudioCapture() {
 	capture = 0;
 	_captureThread.quit();
diff --git a/Telegram/SourceFiles/audio.h b/Telegram/SourceFiles/audio.h
index 6216366ad..ed9de8105 100644
--- a/Telegram/SourceFiles/audio.h
+++ b/Telegram/SourceFiles/audio.h
@@ -119,6 +119,8 @@ public:
 	void start();
 	void stop(bool needResult);
 
+	bool check();
+
 	~AudioCapture();
 
 signals:
diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h
index 3c0894186..1ffc43f1b 100644
--- a/Telegram/SourceFiles/config.h
+++ b/Telegram/SourceFiles/config.h
@@ -17,9 +17,9 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 */
 #pragma once
 
-static const int32 AppVersion = 8017;
-static const wchar_t *AppVersionStr = L"0.8.17";
-static const bool DevChannel = false;
+static const int32 AppVersion = 8018;
+static const wchar_t *AppVersionStr = L"0.8.18";
+static const bool DevChannel = true;
 
 static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
 static const wchar_t *AppName = L"Telegram Desktop";
diff --git a/Telegram/SourceFiles/dropdown.cpp b/Telegram/SourceFiles/dropdown.cpp
index 9202ea116..7b6e216d8 100644
--- a/Telegram/SourceFiles/dropdown.cpp
+++ b/Telegram/SourceFiles/dropdown.cpp
@@ -1384,6 +1384,8 @@ void StickerPanInner::refreshStickers() {
 	_hovers.clear(); _hovers.reserve(sets.size() + 1);
 	_titles.clear(); _titles.reserve(sets.size() + 1);
 
+	LOG(("Stickers: refreshing pan, sets size: %1").arg(sets.size()));
+
 	refreshRecent(false);
 
 	appendSet(CustomStickerSetId);
@@ -1429,10 +1431,12 @@ void StickerPanInner::preloadImages() {
 }
 
 void StickerPanInner::appendSet(uint64 setId) {
+	LOG(("Stickers: appending set %1..").arg(setId));
 	const StickerSets &sets(cStickerSets());
 	StickerSets::const_iterator it = sets.constFind(setId);
 	if (it == sets.cend() || it->stickers.isEmpty()) return;
 
+	LOG(("Stickers: set %1 found! %2 stickers there.").arg(setId).arg(it->stickers.size()));
 	StickerPack pack;
 	pack.reserve(it->stickers.size());
 	for (int32 i = 0, l = it->stickers.size(); i < l; ++i) {
@@ -1448,6 +1452,7 @@ void StickerPanInner::appendSet(uint64 setId) {
 void StickerPanInner::refreshRecent(bool performResize) {
 	clearSelection(true);
 	if (cGetRecentStickers().isEmpty()) {
+		LOG(("Stickers: no recent!"));
 		if (!_setIds.isEmpty() && _setIds.at(0) == RecentStickerSetId) {
 			_setIds.pop_front();
 			_sets.pop_front();
@@ -1455,6 +1460,7 @@ void StickerPanInner::refreshRecent(bool performResize) {
 			_titles.pop_front();
 		}
 	} else {
+		LOG(("Stickers: %1 recent stickers!").arg(cGetRecentStickers().size()));
 		StickerPack recent;
 		recent.reserve(cGetRecentStickers().size());
 		for (int32 i = 0, l = cGetRecentStickers().size(); i < l; ++i) {
@@ -2044,6 +2050,7 @@ void EmojiPan::onRemoveSetSure() {
 				++i;
 			}
 		}
+		LOG(("Stickers: removed one set, %1.").arg(_removingSetId));
 		cRefStickerSets().erase(it);
 		cRefStickerSetsOrder().removeOne(_removingSetId);
 		cSetStickersHash(QByteArray());
diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp
index fa4b9a53f..fdba9d0e2 100644
--- a/Telegram/SourceFiles/historywidget.cpp
+++ b/Telegram/SourceFiles/historywidget.cpp
@@ -1710,18 +1710,20 @@ void HistoryWidget::start() {
 void HistoryWidget::onTextChange() {
 	updateTyping();
 
-	if (_field.getLastText().isEmpty()) {
-		_previewCancelled = false;
-		_send.hide();
-		setMouseTracking(true);
-		mouseMoveEvent(0);
-	} else if (!_field.isHidden()) {
-		_send.show();
-		setMouseTracking(false);
-		_recordAnim.stop();
-		_inRecord = _inField = false;
-		a_recordOver = a_recordDown = anim::fvalue(0, 0);
-		a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c);
+	if (cHasAudioCapture()) {
+		if (_field.getLastText().isEmpty()) {
+			_previewCancelled = false;
+			_send.hide();
+			setMouseTracking(true);
+			mouseMoveEvent(0);
+		} else if (!_field.isHidden() && _send.isHidden()) {
+			_send.show();
+			setMouseTracking(false);
+			_recordAnim.stop();
+			_inRecord = _inField = false;
+			a_recordOver = a_recordDown = anim::fvalue(0, 0);
+			a_recordCancel = anim::cvalue(st::recordCancel->c, st::recordCancel->c);
+		}
 	}
 
 	if (!hist || _synthedTextUpdate) return;
@@ -1849,6 +1851,8 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
 	cSetLastStickersUpdate(getms(true));
 	_stickersUpdateRequest = 0;
 
+	LOG(("Stickers: got from server!"));
+
 	if (stickers.type() != mtpc_messages_allStickers) return;
 	const MTPDmessages_allStickers &d(stickers.c_messages_allStickers());
 
@@ -1857,6 +1861,8 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
 	const QVector<MTPDocument> &d_docs(d.vdocuments.c_vector().v);
 	const QVector<MTPStickerSet> &d_sets(d.vsets.c_vector().v);
 
+	LOG(("Stickers: clearing everything, got all stickers"));
+
 	QByteArray wasHash = cStickersHash();
 	cSetStickersHash(qba(d.vhash));
 
@@ -1995,10 +2001,13 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
 			}
 		}
 	}
+	LOG(("Stickers: now %1 sets, %2 recent").arg(sets.size()).arg(recent.size()));
 	if (added || removed || cStickersHash() != wasHash) {
+		LOG(("Stickers: writing stickers from gotAll!"));
 		Local::writeStickers();
 	}
 	if (writeRecent) {
+		LOG(("Stickers: writing recent stickers from gotAll!"));
 		Local::writeUserSettings();
 	}
 		
@@ -2047,6 +2056,8 @@ void HistoryWidget::stickersGot(const MTPmessages_AllStickers &stickers) {
 bool HistoryWidget::stickersFailed(const RPCError &error) {
 	if (error.type().startsWith(qsl("FLOOD_WAIT_"))) return false;
 
+	LOG(("App Fail: Failed to get stickers!"));
+
 	cSetLastStickersUpdate(getms(true));
 	_stickersUpdateRequest = 0;
 	return true;
@@ -2329,7 +2340,7 @@ void HistoryWidget::updateControlsVisibility() {
 			_toHistoryEnd.show();
 		}
 		if (!histPeer->chat || !histPeer->asChat()->forbidden) {
-			if (_field.getLastText().isEmpty()) {
+			if (cHasAudioCapture() && _field.getLastText().isEmpty()) {
 				_send.hide();
 				setMouseTracking(true);
 				mouseMoveEvent(0);
@@ -3019,7 +3030,7 @@ void HistoryWidget::mouseReleaseEvent(QMouseEvent *e) {
 		_attachDrag = DragStateNone;
 		updateDragAreas();
 	}
-	if (_recording && audioCapture()) {
+	if (_recording && cHasAudioCapture()) {
 		stopRecording(_inField);
 	}
 }
@@ -3796,7 +3807,7 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
 	_replyForwardPressed = QRect(0, _field.y() - st::replyHeight, st::replySkip, st::replyHeight).contains(e->pos());
 	if (_replyForwardPressed && !_replyForwardPreviewCancel.isHidden()) {
 		update(0, _field.y() - st::sendPadding - st::replyHeight, width(), st::replyHeight);
-	} else if (_inRecord && audioCapture()) {
+	} else if (_inRecord && cHasAudioCapture()) {
 		audioCapture()->start();
 
 		_recording = _inField = true;
diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp
index f47c82610..03e302ad6 100644
--- a/Telegram/SourceFiles/localstorage.cpp
+++ b/Telegram/SourceFiles/localstorage.cpp
@@ -2278,16 +2278,20 @@ namespace Local {
 	}
 
 	void importOldRecentStickers() {
+		LOG(("Stickers: importOldStickers()"));
 		if (!_recentStickersKeyOld) return;
 
 		FileReadDescriptor stickers;
 		if (!readEncryptedFile(stickers, _recentStickersKeyOld)) {
+			LOG(("Stickers: could not readEncryptedFile(_recentStickersKeyOld)!"));
 			clearKey(_recentStickersKeyOld);
 			_recentStickersKeyOld = 0;
 			_writeMap();
 			return;
 		}
 		
+		LOG(("Stickers: clearing everything in import-old!"));
+
 		StickerSets &sets(cRefStickerSets());
 		sets.clear();
 		cSetStickerSetsOrder(StickerSetsOrder());
@@ -2334,6 +2338,7 @@ namespace Local {
 			}
 			if (recent.size() < StickerPanPerRow * StickerPanRowsPerPage && qAbs(value) > 1) recent.push_back(qMakePair(doc, qAbs(value)));
 		}
+		LOG(("Stickers: read %1 default stickers and %2 custom stickers").arg(def.stickers.size()).arg(custom.stickers.size()));
 		if (def.stickers.isEmpty()) sets.remove(DefaultStickerSetId);
 		if (custom.stickers.isEmpty()) sets.remove(CustomStickerSetId);
 
@@ -2343,21 +2348,25 @@ namespace Local {
 		clearKey(_recentStickersKeyOld);
 		_recentStickersKeyOld = 0;
 		_writeMap();
+		LOG(("Stickers: writing stickers and clearing old-stickers"));
 	}
 
 	void readStickers() {
+		LOG(("Stickers: readStickers()"));
 		if (!_stickersKey) {
 			return importOldRecentStickers();
 		}
 
 		FileReadDescriptor stickers;
 		if (!readEncryptedFile(stickers, _stickersKey)) {
+			LOG(("Stickers: could not readEncryptedFile(_stickersKey)!"));
 			clearKey(_stickersKey);
 			_stickersKey = 0;
 			_writeMap();
 			return;
 		}
 
+		LOG(("Stickers: clearing everything in read"));
 		StickerSets &sets(cRefStickerSets());
 		sets.clear();
 
@@ -2432,6 +2441,8 @@ namespace Local {
 		}
 
 		cSetStickersHash(hash);
+
+		LOG(("Stickers: read %1 sets").arg(sets.size()));
 	}
 
 	void writeBackground(int32 id, const QImage &img) {
diff --git a/Telegram/SourceFiles/logs.cpp b/Telegram/SourceFiles/logs.cpp
index 968674f89..3018c2652 100644
--- a/Telegram/SourceFiles/logs.cpp
+++ b/Telegram/SourceFiles/logs.cpp
@@ -66,6 +66,7 @@ void debugLogWrite(const char *file, int32 line, const QString &v) {
 
 	{
 		QMutexLocker lock(&debugLogMutex);
+		if (!cDebug() || !debugLogStream) return;
 
 		logsInitDebug(); // maybe need to reopen new file
 
@@ -87,6 +88,7 @@ void tcpLogWrite(const QString &v) {
 
 	{
 		QMutexLocker lock(&debugLogMutex);
+		if (!cDebug() || !tcpLogStream) return;
 
 		logsInitDebug(); // maybe need to reopen new file
 
@@ -100,6 +102,7 @@ void mtpLogWrite(int32 dc, const QString &v) {
 
 	{
 		QMutexLocker lock(&debugLogMutex);
+		if (!cDebug() || !mtpLogStream) return;
 
 		logsInitDebug(); // maybe need to reopen new file
 
@@ -109,7 +112,7 @@ void mtpLogWrite(int32 dc, const QString &v) {
 }
 
 void logWrite(const QString &v) {
-	if (!mainLog.isOpen()) return;
+	if (!mainLogStream) return;
 
 	time_t t = time(NULL);
 	struct tm tm;
@@ -117,6 +120,8 @@ void logWrite(const QString &v) {
 
 	{
 		QMutexLocker lock(&mainLogMutex);
+		if (!mainLogStream) return;
+
 		QString msg(QString("[%1.%2.%3 %4:%5:%6] %7\n").arg(tm.tm_year + 1900).arg(tm.tm_mon + 1, 2, 10, zero).arg(tm.tm_mday, 2, 10, zero).arg(tm.tm_hour, 2, 10, zero).arg(tm.tm_min, 2, 10, zero).arg(tm.tm_sec, 2, 10, zero).arg(v));
 		(*mainLogStream) << msg;
 		mainLogStream->flush();
diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp
index 980c9f6fd..ab9527045 100644
--- a/Telegram/SourceFiles/settings.cpp
+++ b/Telegram/SourceFiles/settings.cpp
@@ -84,6 +84,9 @@ QString gTimeFormat = qsl("hh:mm");
 int32 gAutoLock = 3600;
 bool gHasPasscode = false;
 
+bool gHasAudioPlayer = true;
+bool gHasAudioCapture = true;
+
 DBIEmojiTab gEmojiTab = dbietRecent;
 RecentEmojiPack gRecentEmojis;
 RecentEmojisPreload gRecentEmojisPreload;
diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h
index e405aef5e..c9ee6c3c8 100644
--- a/Telegram/SourceFiles/settings.h
+++ b/Telegram/SourceFiles/settings.h
@@ -134,6 +134,9 @@ DeclareSetting(QString, TimeFormat);
 DeclareSetting(int32, AutoLock);
 DeclareSetting(bool, HasPasscode);
 
+DeclareSetting(bool, HasAudioPlayer);
+DeclareSetting(bool, HasAudioCapture);
+
 inline void cChangeTimeFormat(const QString &newFormat) {
 	if (!newFormat.isEmpty()) cSetTimeFormat(newFormat);
 }
diff --git a/Telegram/Telegram.plist b/Telegram/Telegram.plist
index 05f175be8..537c7efbd 100644
--- a/Telegram/Telegram.plist
+++ b/Telegram/Telegram.plist
@@ -11,7 +11,7 @@
 	<key>CFBundlePackageType</key>
 	<string>APPL</string>
 	<key>CFBundleShortVersionString</key>
-	<string>0.8.17</string>
+	<string>0.8.18</string>
 	<key>CFBundleSignature</key>
 	<string>????</string>
 	<key>CFBundleURLTypes</key>
diff --git a/Telegram/Telegram.rc b/Telegram/Telegram.rc
index a25e749e5..17cdce113 100644
Binary files a/Telegram/Telegram.rc and b/Telegram/Telegram.rc differ
diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj
index 3f5a4e6f1..fa46bff5b 100644
--- a/Telegram/Telegram.vcxproj
+++ b/Telegram/Telegram.vcxproj
@@ -107,8 +107,8 @@
     <Link>
       <SubSystem>Windows</SubSystem>
       <OutputFile>$(OutDir)$(ProjectName).exe</OutputFile>
-      <AdditionalLibraryDirectories>.\..\..\Libraries\lzma\C\Util\LzmaLib\Release;.\..\..\Libraries\libexif-0.6.20\win32\Release;.\..\..\Libraries\libogg-1.3.2\win32\VS2010\Win32\Release;.\..\..\Libraries\opus\win32\VS2010\Win32\Release;.\..\..\Libraries\opusfile\win32\VS2010\Win32\Release;.\..\..\Libraries\mpg123-1.22.1\ports\MSVC++\2010\libmpg123\Release;.\..\..\Libraries\faad2-2.7\libfaad\Release;.\..\..\Libraries\faad2-2.7\common\mp4ff\Release;.\..\..\Libraries\ffmpeg-2.6.3;.\..\..\Libraries\openal-soft\build\Release;.\..\..\Libraries\zlib-1.2.8\contrib\vstudio\vc11\x86\ZlibStatRelease;.\..\..\Libraries\OpenSSL-Win32\lib\VC\static;$(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-      <AdditionalDependencies>kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Core.lib;Qt5Gui.lib;qtharfbuzzng.lib;Qt5Widgets.lib;Qt5Network.lib;Qt5PlatformSupport.lib;platforms\qwindows.lib;imageformats\qwebp.lib;libeay32MT.lib;ssleay32MT.lib;Crypt32.lib;zlibstat.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;LzmaLib.lib;OpenAL32.lib;common.lib;opusfile.lib;opus.lib;libogg_static.lib;libmpg123.lib;libfaad.lib;mp4ff.lib;libavformat\libavformat.a;libavcodec\libavcodec.a;libavutil\libavutil.a;libswresample\libswresample.a;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>.\..\..\Libraries\lzma\C\Util\LzmaLib\Release;.\..\..\Libraries\libexif-0.6.20\win32\Release;.\..\..\Libraries\ffmpeg-2.6.3;.\..\..\Libraries\opus\win32\VS2010\Win32\Release;.\..\..\Libraries\openal-soft\build\Release;.\..\..\Libraries\zlib-1.2.8\contrib\vstudio\vc11\x86\ZlibStatRelease;.\..\..\Libraries\OpenSSL-Win32\lib\VC\static;$(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Core.lib;Qt5Gui.lib;qtharfbuzzng.lib;Qt5Widgets.lib;Qt5Network.lib;Qt5PlatformSupport.lib;platforms\qwindows.lib;imageformats\qwebp.lib;libeay32MT.lib;ssleay32MT.lib;Crypt32.lib;zlibstat.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;LzmaLib.lib;OpenAL32.lib;common.lib;libavformat\libavformat.a;libavcodec\libavcodec.a;libavutil\libavutil.a;libswresample\libswresample.a;opus.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImageHasSafeExceptionHandlers />
       <ImportLibrary>$(SolutionDir)$(Platform)\$(Configuration)Intermediate\$(TargetName).lib</ImportLibrary>
       <ProfileGuidedDatabase>$(IntDir)$(TargetName).pgd</ProfileGuidedDatabase>
@@ -134,8 +134,8 @@
     <Link>
       <SubSystem>Windows</SubSystem>
       <OutputFile>$(OutDir)$(ProjectName).exe</OutputFile>
-      <AdditionalLibraryDirectories>.\..\..\Libraries\lzma\C\Util\LzmaLib\Release;.\..\..\Libraries\libexif-0.6.20\win32\Release;.\..\..\Libraries\ffmpeg-2.6.3;.\..\..\Libraries\openal-soft\build\Release;.\..\..\Libraries\zlib-1.2.8\contrib\vstudio\vc11\x86\ZlibStatRelease;.\..\..\Libraries\OpenSSL-Win32\lib\VC\static;$(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
-      <AdditionalDependencies>kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Core.lib;Qt5Gui.lib;qtharfbuzzng.lib;Qt5Widgets.lib;Qt5Network.lib;Qt5PlatformSupport.lib;platforms\qwindows.lib;imageformats\qwebp.lib;libeay32MT.lib;ssleay32MT.lib;Crypt32.lib;zlibstat.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;LzmaLib.lib;OpenAL32.lib;common.lib;libavformat\libavformat.a;libavcodec\libavcodec.a;libavutil\libavutil.a;libswresample\libswresample.a;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalLibraryDirectories>.\..\..\Libraries\lzma\C\Util\LzmaLib\Release;.\..\..\Libraries\libexif-0.6.20\win32\Release;.\..\..\Libraries\ffmpeg-2.6.3;.\..\..\Libraries\opus\win32\VS2010\Win32\Release;.\..\..\Libraries\openal-soft\build\Release;.\..\..\Libraries\zlib-1.2.8\contrib\vstudio\vc11\x86\ZlibStatRelease;.\..\..\Libraries\OpenSSL-Win32\lib\VC\static;$(QTDIR)\lib;$(QTDIR)\plugins;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+      <AdditionalDependencies>kernel32.lib;user32.lib;shell32.lib;uuid.lib;ole32.lib;advapi32.lib;ws2_32.lib;gdi32.lib;comdlg32.lib;oleaut32.lib;imm32.lib;winmm.lib;qtmain.lib;glu32.lib;opengl32.lib;Strmiids.lib;Qt5Core.lib;Qt5Gui.lib;qtharfbuzzng.lib;Qt5Widgets.lib;Qt5Network.lib;Qt5PlatformSupport.lib;platforms\qwindows.lib;imageformats\qwebp.lib;libeay32MT.lib;ssleay32MT.lib;Crypt32.lib;zlibstat.lib;lib_exif.lib;UxTheme.lib;DbgHelp.lib;LzmaLib.lib;OpenAL32.lib;common.lib;libavformat\libavformat.a;libavcodec\libavcodec.a;libavutil\libavutil.a;libswresample\libswresample.a;opus.lib;celt.lib;silk_common.lib;silk_float.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <ImageHasSafeExceptionHandlers>
       </ImageHasSafeExceptionHandlers>
       <ImportLibrary>$(SolutionDir)$(Platform)\$(Configuration)Intermediate\$(TargetName).lib</ImportLibrary>
diff --git a/Telegram/Telegram.xcodeproj/project.pbxproj b/Telegram/Telegram.xcodeproj/project.pbxproj
index eb946c1f9..bf2d220bd 100644
--- a/Telegram/Telegram.xcodeproj/project.pbxproj
+++ b/Telegram/Telegram.xcodeproj/project.pbxproj
@@ -1693,7 +1693,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 0.8.17;
+				CURRENT_PROJECT_VERSION = 0.8.18;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
 				GCC_OPTIMIZATION_LEVEL = 0;
@@ -1711,7 +1711,7 @@
 			buildSettings = {
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				COPY_PHASE_STRIP = YES;
-				CURRENT_PROJECT_VERSION = 0.8.17;
+				CURRENT_PROJECT_VERSION = 0.8.18;
 				GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
 				GCC_OPTIMIZATION_LEVEL = fast;
 				GCC_PREFIX_HEADER = ./SourceFiles/stdafx.h;
@@ -1737,10 +1737,10 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				CODE_SIGN_IDENTITY = "";
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 0.8.17;
+				CURRENT_PROJECT_VERSION = 0.8.18;
 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
 				DYLIB_COMPATIBILITY_VERSION = 0.8;
-				DYLIB_CURRENT_VERSION = 0.8.17;
+				DYLIB_CURRENT_VERSION = 0.8.18;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				FRAMEWORK_SEARCH_PATHS = "";
 				GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
@@ -1880,10 +1880,10 @@
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				CODE_SIGN_IDENTITY = "";
 				COPY_PHASE_STRIP = NO;
-				CURRENT_PROJECT_VERSION = 0.8.17;
+				CURRENT_PROJECT_VERSION = 0.8.18;
 				DEBUG_INFORMATION_FORMAT = dwarf;
 				DYLIB_COMPATIBILITY_VERSION = 0.8;
-				DYLIB_CURRENT_VERSION = 0.8.17;
+				DYLIB_CURRENT_VERSION = 0.8.18;
 				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				FRAMEWORK_SEARCH_PATHS = "";
 				GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
diff --git a/Telegram/Version.sh b/Telegram/Version.sh
index e0ab93ebb..cf96f1b3c 100755
--- a/Telegram/Version.sh
+++ b/Telegram/Version.sh
@@ -1,2 +1,2 @@
-echo 8017 0.8.17 0
+echo 8018 0.8.18 1
 # AppVersion AppVersionStr DevChannel
diff --git a/Telegram/_openal_patch/Alc/backends/winmm.c b/Telegram/_openal_patch/Alc/backends/winmm.c
new file mode 100644
index 000000000..81ce905ef
--- /dev/null
+++ b/Telegram/_openal_patch/Alc/backends/winmm.c
@@ -0,0 +1,716 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 1999-2007 by authors.
+ * This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Library General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ *  License along with this library; if not, write to the
+ *  Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ * Or go to http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+
+#ifndef WAVE_FORMAT_IEEE_FLOAT
+#define WAVE_FORMAT_IEEE_FLOAT  0x0003
+#endif
+
+
+typedef struct {
+    // MMSYSTEM Device
+    volatile ALboolean killNow;
+    althrd_t thread;
+
+    RefCount WaveBuffersCommitted;
+    WAVEHDR WaveBuffer[4];
+
+    union {
+        HWAVEIN  In;
+        HWAVEOUT Out;
+    } WaveHandle;
+
+    WAVEFORMATEX Format;
+
+    RingBuffer *Ring;
+} WinMMData;
+
+
+TYPEDEF_VECTOR(al_string, vector_al_string)
+static vector_al_string PlaybackDevices;
+static vector_al_string CaptureDevices;
+
+static void clear_devlist(vector_al_string *list)
+{
+    VECTOR_FOR_EACH(al_string, *list, al_string_deinit);
+    VECTOR_RESIZE(*list, 0);
+}
+
+
+static void ProbePlaybackDevices(void)
+{
+    al_string *iter, *end;
+    ALuint numdevs;
+    ALuint i;
+
+    clear_devlist(&PlaybackDevices);
+
+    numdevs = waveOutGetNumDevs();
+    VECTOR_RESERVE(PlaybackDevices, numdevs);
+    for(i = 0;i < numdevs;i++)
+    {
+        WAVEOUTCAPSW WaveCaps;
+        al_string dname;
+
+        AL_STRING_INIT(dname);
+        if(waveOutGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
+        {
+            ALuint count = 0;
+            do {
+                al_string_copy_wcstr(&dname, WaveCaps.szPname);
+                if(count != 0)
+                {
+                    char str[64];
+                    snprintf(str, sizeof(str), " #%d", count+1);
+                    al_string_append_cstr(&dname, str);
+                }
+                count++;
+
+                iter = VECTOR_ITER_BEGIN(PlaybackDevices);
+                end = VECTOR_ITER_END(PlaybackDevices);
+                for(;iter != end;iter++)
+                {
+                    if(al_string_cmp(*iter, dname) == 0)
+                        break;
+                }
+            } while(iter != end);
+
+            TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+        }
+        VECTOR_PUSH_BACK(PlaybackDevices, dname);
+    }
+}
+
+static void ProbeCaptureDevices(void)
+{
+    al_string *iter, *end;
+    ALuint numdevs;
+    ALuint i;
+
+    clear_devlist(&CaptureDevices);
+
+    numdevs = waveInGetNumDevs();
+    VECTOR_RESERVE(CaptureDevices, numdevs);
+    for(i = 0;i < numdevs;i++)
+    {
+        WAVEINCAPSW WaveCaps;
+        al_string dname;
+
+        AL_STRING_INIT(dname);
+        if(waveInGetDevCapsW(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
+        {
+            ALuint count = 0;
+            do {
+                al_string_copy_wcstr(&dname, WaveCaps.szPname);
+                if(count != 0)
+                {
+                    char str[64];
+                    snprintf(str, sizeof(str), " #%d", count+1);
+                    al_string_append_cstr(&dname, str);
+                }
+                count++;
+
+                iter = VECTOR_ITER_BEGIN(CaptureDevices);
+                end = VECTOR_ITER_END(CaptureDevices);
+                for(;iter != end;iter++)
+                {
+                    if(al_string_cmp(*iter, dname) == 0)
+                        break;
+                }
+            } while(iter != end);
+
+            TRACE("Got device \"%s\", ID %u\n", al_string_get_cstr(dname), i);
+        }
+        VECTOR_PUSH_BACK(CaptureDevices, dname);
+    }
+}
+
+
+/*
+    WaveOutProc
+
+    Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
+    returns to the application (for more data)
+*/
+static void CALLBACK WaveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
+{
+    ALCdevice *Device = (ALCdevice*)instance;
+    WinMMData *data = Device->ExtraData;
+
+    if(msg != WOM_DONE)
+        return;
+
+    DecrementRef(&data->WaveBuffersCommitted);
+    PostThreadMessage(data->thread, msg, 0, param1);
+}
+
+FORCE_ALIGN static int PlaybackThreadProc(void *arg)
+{
+    ALCdevice *Device = (ALCdevice*)arg;
+    WinMMData *data = Device->ExtraData;
+    WAVEHDR *WaveHdr;
+    MSG msg;
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    while(GetMessage(&msg, NULL, 0, 0))
+    {
+        if(msg.message != WOM_DONE)
+            continue;
+
+        if(data->killNow)
+        {
+            if(ReadRef(&data->WaveBuffersCommitted) == 0)
+                break;
+            continue;
+        }
+
+        WaveHdr = ((WAVEHDR*)msg.lParam);
+        aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength /
+                                            data->Format.nBlockAlign);
+
+        // Send buffer back to play more data
+        waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR));
+        IncrementRef(&data->WaveBuffersCommitted);
+    }
+
+    return 0;
+}
+
+/*
+    WaveInProc
+
+    Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
+    returns to the application (with more data)
+*/
+static void CALLBACK WaveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
+{
+    ALCdevice *Device = (ALCdevice*)instance;
+    WinMMData *data = Device->ExtraData;
+
+    if(msg != WIM_DATA)
+        return;
+
+    DecrementRef(&data->WaveBuffersCommitted);
+    PostThreadMessage(data->thread, msg, 0, param1);
+}
+
+static int CaptureThreadProc(void *arg)
+{
+    ALCdevice *Device = (ALCdevice*)arg;
+    WinMMData *data = Device->ExtraData;
+    WAVEHDR *WaveHdr;
+    MSG msg;
+
+    althrd_setname(althrd_current(), "alsoft-record");
+
+    if (!data->killNow) while(GetMessage(&msg, NULL, 0, 0))
+    {
+        if(msg.message != WIM_DATA)
+            continue;
+        /* Don't wait for other buffers to finish before quitting. We're
+         * closing so we don't need them. */
+        if(data->killNow)
+            break;
+
+        WaveHdr = ((WAVEHDR*)msg.lParam);
+        WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData,
+                        WaveHdr->dwBytesRecorded/data->Format.nBlockAlign);
+
+        // Send buffer back to capture more data
+        waveInAddBuffer(data->WaveHandle.In, WaveHdr, sizeof(WAVEHDR));
+        IncrementRef(&data->WaveBuffersCommitted);
+    }
+
+    return 0;
+}
+
+
+static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
+{
+    WinMMData *data = NULL;
+    const al_string *iter, *end;
+    UINT DeviceID;
+    MMRESULT res;
+
+    if(VECTOR_SIZE(PlaybackDevices) == 0)
+        ProbePlaybackDevices();
+
+    // Find the Device ID matching the deviceName if valid
+    iter = VECTOR_ITER_BEGIN(PlaybackDevices);
+    end = VECTOR_ITER_END(PlaybackDevices);
+    for(;iter != end;iter++)
+    {
+        if(!al_string_empty(*iter) &&
+           (!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0))
+        {
+            DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
+            break;
+        }
+    }
+    if(iter == end)
+        return ALC_INVALID_VALUE;
+
+    data = calloc(1, sizeof(*data));
+    if(!data)
+        return ALC_OUT_OF_MEMORY;
+    Device->ExtraData = data;
+
+retry_open:
+    memset(&data->Format, 0, sizeof(WAVEFORMATEX));
+    if(Device->FmtType == DevFmtFloat)
+    {
+        data->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+        data->Format.wBitsPerSample = 32;
+    }
+    else
+    {
+        data->Format.wFormatTag = WAVE_FORMAT_PCM;
+        if(Device->FmtType == DevFmtUByte || Device->FmtType == DevFmtByte)
+            data->Format.wBitsPerSample = 8;
+        else
+            data->Format.wBitsPerSample = 16;
+    }
+    data->Format.nChannels = ((Device->FmtChans == DevFmtMono) ? 1 : 2);
+    data->Format.nBlockAlign = data->Format.wBitsPerSample *
+                               data->Format.nChannels / 8;
+    data->Format.nSamplesPerSec = Device->Frequency;
+    data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
+                                   data->Format.nBlockAlign;
+    data->Format.cbSize = 0;
+
+    if((res=waveOutOpen(&data->WaveHandle.Out, DeviceID, &data->Format, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
+    {
+        if(Device->FmtType == DevFmtFloat)
+        {
+            Device->FmtType = DevFmtShort;
+            goto retry_open;
+        }
+        ERR("waveOutOpen failed: %u\n", res);
+        goto failure;
+    }
+
+    al_string_copy(&Device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
+    return ALC_NO_ERROR;
+
+failure:
+    if(data->WaveHandle.Out)
+        waveOutClose(data->WaveHandle.Out);
+
+    free(data);
+    Device->ExtraData = NULL;
+    return ALC_INVALID_VALUE;
+}
+
+static void WinMMClosePlayback(ALCdevice *device)
+{
+    WinMMData *data = (WinMMData*)device->ExtraData;
+
+    // Close the Wave device
+    waveOutClose(data->WaveHandle.Out);
+    data->WaveHandle.Out = 0;
+
+    free(data);
+    device->ExtraData = NULL;
+}
+
+static ALCboolean WinMMResetPlayback(ALCdevice *device)
+{
+    WinMMData *data = (WinMMData*)device->ExtraData;
+
+    device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
+                                  data->Format.nSamplesPerSec /
+                                  device->Frequency);
+    device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
+    device->NumUpdates = 4;
+    device->Frequency = data->Format.nSamplesPerSec;
+
+    if(data->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
+    {
+        if(data->Format.wBitsPerSample == 32)
+            device->FmtType = DevFmtFloat;
+        else
+        {
+            ERR("Unhandled IEEE float sample depth: %d\n", data->Format.wBitsPerSample);
+            return ALC_FALSE;
+        }
+    }
+    else if(data->Format.wFormatTag == WAVE_FORMAT_PCM)
+    {
+        if(data->Format.wBitsPerSample == 16)
+            device->FmtType = DevFmtShort;
+        else if(data->Format.wBitsPerSample == 8)
+            device->FmtType = DevFmtUByte;
+        else
+        {
+            ERR("Unhandled PCM sample depth: %d\n", data->Format.wBitsPerSample);
+            return ALC_FALSE;
+        }
+    }
+    else
+    {
+        ERR("Unhandled format tag: 0x%04x\n", data->Format.wFormatTag);
+        return ALC_FALSE;
+    }
+
+    if(data->Format.nChannels == 2)
+        device->FmtChans = DevFmtStereo;
+    else if(data->Format.nChannels == 1)
+        device->FmtChans = DevFmtMono;
+    else
+    {
+        ERR("Unhandled channel count: %d\n", data->Format.nChannels);
+        return ALC_FALSE;
+    }
+    SetDefaultWFXChannelOrder(device);
+
+    return ALC_TRUE;
+}
+
+static ALCboolean WinMMStartPlayback(ALCdevice *device)
+{
+    WinMMData *data = (WinMMData*)device->ExtraData;
+    ALbyte *BufferData;
+    ALint BufferSize;
+    ALuint i;
+
+    data->killNow = AL_FALSE;
+    if(althrd_create(&data->thread, PlaybackThreadProc, device) != althrd_success)
+        return ALC_FALSE;
+
+    InitRef(&data->WaveBuffersCommitted, 0);
+
+    // Create 4 Buffers
+    BufferSize  = device->UpdateSize*device->NumUpdates / 4;
+    BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
+
+    BufferData = calloc(4, BufferSize);
+    for(i = 0;i < 4;i++)
+    {
+        memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
+        data->WaveBuffer[i].dwBufferLength = BufferSize;
+        data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
+                                      (data->WaveBuffer[i-1].lpData +
+                                       data->WaveBuffer[i-1].dwBufferLength));
+        waveOutPrepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
+        waveOutWrite(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
+        IncrementRef(&data->WaveBuffersCommitted);
+    }
+
+    return ALC_TRUE;
+}
+
+static void WinMMStopPlayback(ALCdevice *device)
+{
+    WinMMData *data = (WinMMData*)device->ExtraData;
+    void *buffer = NULL;
+    int i;
+
+    if(data->killNow)
+        return;
+
+    // Set flag to stop processing headers
+    data->killNow = AL_TRUE;
+    althrd_join(data->thread, &i);
+
+    // Release the wave buffers
+    for(i = 0;i < 4;i++)
+    {
+        waveOutUnprepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
+        if(i == 0) buffer = data->WaveBuffer[i].lpData;
+        data->WaveBuffer[i].lpData = NULL;
+    }
+    free(buffer);
+}
+
+
+static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
+{
+    const al_string *iter, *end;
+    ALbyte *BufferData = NULL;
+    DWORD CapturedDataSize;
+    WinMMData *data = NULL;
+    ALint BufferSize;
+    UINT DeviceID;
+    MMRESULT res;
+    ALuint i;
+
+    if(VECTOR_SIZE(CaptureDevices) == 0)
+        ProbeCaptureDevices();
+
+    // Find the Device ID matching the deviceName if valid
+    iter = VECTOR_ITER_BEGIN(CaptureDevices);
+    end = VECTOR_ITER_END(CaptureDevices);
+    for(;iter != end;iter++)
+    {
+        if(!al_string_empty(*iter) &&
+           (!deviceName || al_string_cmp_cstr(*iter, deviceName) == 0))
+        {
+            DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
+            break;
+        }
+    }
+    if(iter == end)
+        return ALC_INVALID_VALUE;
+
+    switch(Device->FmtChans)
+    {
+        case DevFmtMono:
+        case DevFmtStereo:
+            break;
+
+        case DevFmtQuad:
+        case DevFmtX51:
+        case DevFmtX51Side:
+        case DevFmtX61:
+        case DevFmtX71:
+            return ALC_INVALID_ENUM;
+    }
+
+    switch(Device->FmtType)
+    {
+        case DevFmtUByte:
+        case DevFmtShort:
+        case DevFmtInt:
+        case DevFmtFloat:
+            break;
+
+        case DevFmtByte:
+        case DevFmtUShort:
+        case DevFmtUInt:
+            return ALC_INVALID_ENUM;
+    }
+
+    data = calloc(1, sizeof(*data));
+    if(!data)
+        return ALC_OUT_OF_MEMORY;
+    Device->ExtraData = data;
+
+    memset(&data->Format, 0, sizeof(WAVEFORMATEX));
+    data->Format.wFormatTag = ((Device->FmtType == DevFmtFloat) ?
+                               WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
+    data->Format.nChannels = ChannelsFromDevFmt(Device->FmtChans);
+    data->Format.wBitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
+    data->Format.nBlockAlign = data->Format.wBitsPerSample *
+                               data->Format.nChannels / 8;
+    data->Format.nSamplesPerSec = Device->Frequency;
+    data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
+                                   data->Format.nBlockAlign;
+    data->Format.cbSize = 0;
+
+    if((res=waveInOpen(&data->WaveHandle.In, DeviceID, &data->Format, (DWORD_PTR)&WaveInProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
+    {
+        ERR("waveInOpen failed: %u\n", res);
+        goto failure;
+    }
+
+    // Allocate circular memory buffer for the captured audio
+    CapturedDataSize = Device->UpdateSize*Device->NumUpdates;
+
+    // Make sure circular buffer is at least 100ms in size
+    if(CapturedDataSize < (data->Format.nSamplesPerSec / 10))
+        CapturedDataSize = data->Format.nSamplesPerSec / 10;
+
+    data->Ring = CreateRingBuffer(data->Format.nBlockAlign, CapturedDataSize);
+    if(!data->Ring)
+        goto failure;
+
+    InitRef(&data->WaveBuffersCommitted, 0);
+
+    // Create 4 Buffers of 50ms each
+    BufferSize = data->Format.nAvgBytesPerSec / 20;
+    BufferSize -= (BufferSize % data->Format.nBlockAlign);
+
+    BufferData = calloc(4, BufferSize);
+    if(!BufferData)
+        goto failure;
+
+    for(i = 0;i < 4;i++)
+    {
+        memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
+        data->WaveBuffer[i].dwBufferLength = BufferSize;
+        data->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
+                                      (data->WaveBuffer[i-1].lpData +
+                                       data->WaveBuffer[i-1].dwBufferLength));
+        data->WaveBuffer[i].dwFlags = 0;
+        data->WaveBuffer[i].dwLoops = 0;
+        waveInPrepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
+        waveInAddBuffer(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
+        IncrementRef(&data->WaveBuffersCommitted);
+    }
+
+    if(althrd_create(&data->thread, CaptureThreadProc, Device) != althrd_success)
+        goto failure;
+
+    al_string_copy(&Device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
+    return ALC_NO_ERROR;
+
+failure:
+    if(BufferData)
+    {
+        for(i = 0;i < 4;i++)
+            waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
+        free(BufferData);
+    }
+
+    if(data->Ring)
+        DestroyRingBuffer(data->Ring);
+
+    if(data->WaveHandle.In)
+        waveInClose(data->WaveHandle.In);
+
+    free(data);
+    Device->ExtraData = NULL;
+    return ALC_INVALID_VALUE;
+}
+
+static void WinMMCloseCapture(ALCdevice *Device)
+{
+    WinMMData *data = (WinMMData*)Device->ExtraData;
+    void *buffer = NULL;
+    int i;
+
+    /* Tell the processing thread to quit and wait for it to do so. */
+    data->killNow = AL_TRUE;
+    PostThreadMessage(data->thread, WM_QUIT, 0, 0);
+
+    althrd_join(data->thread, &i);
+
+    /* Make sure capture is stopped and all pending buffers are flushed. */
+    waveInReset(data->WaveHandle.In);
+
+    // Release the wave buffers
+    for(i = 0;i < 4;i++)
+    {
+        waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
+        if(i == 0) buffer = data->WaveBuffer[i].lpData;
+        data->WaveBuffer[i].lpData = NULL;
+    }
+    free(buffer);
+
+    DestroyRingBuffer(data->Ring);
+    data->Ring = NULL;
+
+    // Close the Wave device
+    waveInClose(data->WaveHandle.In);
+    data->WaveHandle.In = 0;
+
+    free(data);
+    Device->ExtraData = NULL;
+}
+
+static void WinMMStartCapture(ALCdevice *Device)
+{
+    WinMMData *data = (WinMMData*)Device->ExtraData;
+    waveInStart(data->WaveHandle.In);
+}
+
+static void WinMMStopCapture(ALCdevice *Device)
+{
+    WinMMData *data = (WinMMData*)Device->ExtraData;
+    waveInStop(data->WaveHandle.In);
+}
+
+static ALCenum WinMMCaptureSamples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
+{
+    WinMMData *data = (WinMMData*)Device->ExtraData;
+    ReadRingBuffer(data->Ring, Buffer, Samples);
+    return ALC_NO_ERROR;
+}
+
+static ALCuint WinMMAvailableSamples(ALCdevice *Device)
+{
+    WinMMData *data = (WinMMData*)Device->ExtraData;
+    return RingBufferSize(data->Ring);
+}
+
+
+static inline void AppendAllDevicesList2(const al_string *name)
+{
+    if(!al_string_empty(*name))
+        AppendAllDevicesList(al_string_get_cstr(*name));
+}
+static inline void AppendCaptureDeviceList2(const al_string *name)
+{
+    if(!al_string_empty(*name))
+        AppendCaptureDeviceList(al_string_get_cstr(*name));
+}
+
+static const BackendFuncs WinMMFuncs = {
+    WinMMOpenPlayback,
+    WinMMClosePlayback,
+    WinMMResetPlayback,
+    WinMMStartPlayback,
+    WinMMStopPlayback,
+    WinMMOpenCapture,
+    WinMMCloseCapture,
+    WinMMStartCapture,
+    WinMMStopCapture,
+    WinMMCaptureSamples,
+    WinMMAvailableSamples,
+    ALCdevice_GetLatencyDefault
+};
+
+ALCboolean alcWinMMInit(BackendFuncs *FuncList)
+{
+    VECTOR_INIT(PlaybackDevices);
+    VECTOR_INIT(CaptureDevices);
+
+    *FuncList = WinMMFuncs;
+    return ALC_TRUE;
+}
+
+void alcWinMMDeinit()
+{
+    clear_devlist(&PlaybackDevices);
+    VECTOR_DEINIT(PlaybackDevices);
+
+    clear_devlist(&CaptureDevices);
+    VECTOR_DEINIT(CaptureDevices);
+}
+
+void alcWinMMProbe(enum DevProbe type)
+{
+    switch(type)
+    {
+        case ALL_DEVICE_PROBE:
+            ProbePlaybackDevices();
+            VECTOR_FOR_EACH(const al_string, PlaybackDevices, AppendAllDevicesList2);
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+            ProbeCaptureDevices();
+            VECTOR_FOR_EACH(const al_string, CaptureDevices, AppendCaptureDeviceList2);
+            break;
+    }
+}