diff --git a/MSVC.md b/MSVC.md
index 303a4122c7..c93ac3d237 100644
--- a/MSVC.md
+++ b/MSVC.md
@@ -66,13 +66,23 @@ or download in ZIP and extract to **D:\TBuild\Libraries\**, rename **libexif-0.6
 * Build Debug configuration
 * Build Release configuration
 
-####OpenAL Soft
+####OpenAL Soft, slightly patched
 
 Get sources by git – in [Git Bash](http://git-scm.com/downloads) go to **/d/tbuild/libraries** and run
 
     git clone git://repo.or.cz/openal-soft.git
 
-to have **D:\TBuild\Libraries\openal-soft\CMakeLists.txt**
+to have **D:\TBuild\Libraries\openal-soft\CMakeLists.txt**, then in [Git Bash](http://git-scm.com/downloads) go to **/d/tbuild/libraries/openal-soft** and run
+
+    git checkout 9479ea656b
+
+Apply patch
+
+* OR copy (with overwrite!) everything from **D:\\TBuild\\tdesktop\\\_openal\_patch\\** to **D:\\TBuild\\Libraries\\openal-soft\\**
+* OR in Git Bash go to **/d/tbuild/libraries/openal-soft/** and run
+
+    git apply ./../../tdesktop/Telegram/_openal_patch.diff
+
 
 #####Building library
 
@@ -138,9 +148,9 @@ Extract to **D:\TBuild\Libraries\**, rename **qt-everywhere-opensource-src-5.4.0
 Apply patch
 
 * OR copy (with overwrite!) everything from **D:\TBuild\tdesktop\\\_qt\_5\_4\_0\_patch\** to **D:\TBuild\Libraries\QtStatic\**
-* OR copy **D:\TBuild\tdesktop\\\_qt\_5\_4\_0\_patch.diff** to **D:\TBuild\Libraries\QtStatic\**, go there in Git Bash and run
+* OR in Git Bash go to **/d/TBuild/Libraries/QtStatic/** and run
 
-    git apply _qt_5_4_0_patch.diff
+    git apply ./../../tdesktop/Telegram/_qt_5_4_0_patch.diff
 
 #####Building library
 
diff --git a/Telegram/SourceFiles/_other/updater.cpp b/Telegram/SourceFiles/_other/updater.cpp
index c4bcb97e75..b9b7a25878 100644
--- a/Telegram/SourceFiles/_other/updater.cpp
+++ b/Telegram/SourceFiles/_other/updater.cpp
@@ -96,12 +96,11 @@ void writeLog(const wstring &msg) {
 	}
 }
 
-void delFolder() {
-	wstring delPath = L"tupdates\\ready", delFolder = L"tupdates";
+void fullClearPath(const wstring &dir) {
 	WCHAR path[4096];
-	memcpy(path, delPath.c_str(), (delPath.size() + 1) * sizeof(WCHAR));
-	path[delPath.size() + 1] = 0;
-	writeLog(L"Fully clearing path '" + delPath + L"'..");
+	memcpy(path, dir.c_str(), (dir.size() + 1) * sizeof(WCHAR));
+	path[dir.size() + 1] = 0;
+	writeLog(L"Fully clearing path '" + dir + L"'..");
 	SHFILEOPSTRUCT file_op = {
 		NULL,
 		FO_DELETE,
@@ -116,13 +115,27 @@ void delFolder() {
 	};
 	int res = SHFileOperation(&file_op);
 	if (res) writeLog(L"Error: failed to clear path! :(");
+}
+
+void delFolder() {
+	wstring delPathOld = L"tupdates\\ready", delPath = L"tupdates\\temp", delFolder = L"tupdates";
+	fullClearPath(delPathOld);
+	fullClearPath(delPath);
 	RemoveDirectory(delFolder.c_str());
 }
 
 bool update() {
 	writeLog(L"Update started..");
 
-	wstring updDir = L"tupdates\\ready";
+	wstring updDir = L"tupdates\\temp", readyFilePath = L"tupdates\\temp\\ready";
+	{
+		HANDLE readyFile = CreateFile(readyFilePath.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+		if (readyFile != INVALID_HANDLE_VALUE) {
+			CloseHandle(readyFile);
+		} else {
+			updDir = L"tupdates\\ready"; // old
+		}
+	}
 
 	deque<wstring> dirs;
 	dirs.push_back(updDir);
@@ -166,10 +179,13 @@ bool update() {
 					writeLog(L"Error: bad update, has Updater.exe! '" + tofname + L"' equal '" + exeName + L"'");
 					delFolder();
 					return false;
+				} else if (equal(fname, readyFilePath)) {
+					writeLog(L"Skipped ready file '" + fname + L"'");
+				} else {
+					from.push_back(fname);
+					to.push_back(tofname);
+					writeLog(L"Added file '" + fname + L"' to be copied to '" + tofname + L"'");
 				}
-				from.push_back(fname);
-				to.push_back(tofname);
-				writeLog(L"Added file '" + fname + L"' to be copied to '" + tofname + L"'");
 			}
 		} while (FindNextFile(findHandle, &findData));
 		DWORD errorCode = GetLastError();
@@ -357,7 +373,7 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
 					updateRegistry();
 				}
 				if (writeprotected) { // if we can't clear all tupdates\ready (Updater.exe is there) - clear only version
-					if (DeleteFile(L"tupdates\\ready\\tdata\\version")) {
+					if (DeleteFile(L"tupdates\\temp\\tdata\\version") || DeleteFile(L"tupdates\\ready\\tdata\\version")) {
 						writeLog(L"Version file deleted!");
 					} else {
 						writeLog(L"Error: could not delete version file");
@@ -386,7 +402,6 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
 
 		HRESULT hres = CoInitialize(0);
 		if (SUCCEEDED(hres)) {
-			wstring lnk = L"tupdates\\ready\\temp.lnk";
 			IShellLink* psl;
 			HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
 			if (SUCCEEDED(hres)) {
@@ -401,7 +416,12 @@ int APIENTRY WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdParama
 				hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
 
 				if (SUCCEEDED(hres)) {
+					wstring lnk = L"tupdates\\temp\\temp.lnk";
 					hres = ppf->Save(lnk.c_str(), TRUE);
+					if (!SUCCEEDED(hres)) {
+						lnk = L"tupdates\\ready\\temp.lnk"; // old
+						hres = ppf->Save(lnk.c_str(), TRUE);
+					}
 					ppf->Release();
 
 					if (SUCCEEDED(hres)) {
diff --git a/Telegram/SourceFiles/_other/updater_linux.cpp b/Telegram/SourceFiles/_other/updater_linux.cpp
index 09ab4c676e..d53aa6b7f1 100644
--- a/Telegram/SourceFiles/_other/updater_linux.cpp
+++ b/Telegram/SourceFiles/_other/updater_linux.cpp
@@ -136,7 +136,7 @@ bool remove_directory(const string &path) { // from http://stackoverflow.com/que
 
     if (!d) {
         writeLog("Could not open dir '%s'", path.c_str());
-        return false;
+        return (errno == ENOENT);
     }
 
     while (struct dirent *p = readdir(d)) {
@@ -201,18 +201,30 @@ bool equal(string a, string b) {
 }
 
 void delFolder() {
-    string delPath = workDir + "tupdates/ready", delFolder = workDir + "tupdates";
-    writeLog("Fully clearing path '%s'..", delPath.c_str());
-    if (!remove_directory(delPath)) {
-        writeLog("Error: failed to clear path! :(");
+    string delPathOld = workDir + "tupdates/ready", delPath = workDir + "tupdates/temp", delFolder = workDir + "tupdates";
+    writeLog("Fully clearing old path '%s'..", delPathOld.c_str());
+    if (!remove_directory(delPathOld)) {
+        writeLog("Error: failed to clear old path! :(");
     }
-    rmdir(delFolder.c_str());
+	writeLog("Fully clearing path '%s'..", delPath.c_str());
+	if (!remove_directory(delPath)) {
+		writeLog("Error: failed to clear path! :(");
+	}
+	rmdir(delFolder.c_str());
 }
 
 bool update() {
     writeLog("Update started..");
 
-    string updDir = workDir + "tupdates/ready";
+	string updDir = workDir + "tupdates/temp", readyFilePath = workDir + "tupdates/temp/ready";
+	{
+		FILE *readyFile = fopen(readyFilePath.c_str(), "rb");
+		if (readyFile) {
+			fclose(readyFile);
+		} else {
+			updDir = workDir + "tupdates/ready"; // old
+		}
+	}
 
     deque<string> dirs;
 	dirs.push_back(updDir);
@@ -253,9 +265,13 @@ bool update() {
                         delFolder();
                         return false;
                     }
-                    from.push_back(fname);
-                    to.push_back(tofname);
-                    writeLog("Added file '%s' to be copied to '%s'", fname.c_str(), tofname.c_str());
+					if (fname == readyFilePath) {
+						writeLog("Skipped ready file '%s'", fname.c_str());
+					} else {
+						from.push_back(fname);
+						to.push_back(tofname);
+						writeLog("Added file '%s' to be copied to '%s'", fname.c_str(), tofname.c_str());
+					}
                 }
             } else {
                 writeLog("Could not get stat() for file %s", fname.c_str());
@@ -336,7 +352,7 @@ int main(int argc, char *argv[]) {
             exeDir = exeName.substr(0, exeName.size() - 7);
             writeLog("Exe dir is: %s", exeDir.c_str());
             if (needupdate) {
-                if (workDir.empty()) { // old app launched
+                if (workDir.empty()) { // old app launched, update prepared in tupdates/ready (not in tupdates/temp)
                     writeLog("No workdir, trying to figure it out");
                     struct passwd *pw = getpwuid(getuid());
                     if (pw && pw->pw_dir && strlen(pw->pw_dir)) {
diff --git a/Telegram/SourceFiles/_other/updater_osx.m b/Telegram/SourceFiles/_other/updater_osx.m
index 2839a037ba..1d0a1d9a77 100644
--- a/Telegram/SourceFiles/_other/updater_osx.m
+++ b/Telegram/SourceFiles/_other/updater_osx.m
@@ -55,7 +55,8 @@ void writeLog(NSString *msg) {
 }
 
 void delFolder() {
-	[[NSFileManager defaultManager] removeItemAtPath:[workDir stringByAppendingString:@"tupdates/ready"] error:nil];
+	[[NSFileManager defaultManager] removeItemAtPath:[workDir stringByAppendingString:@"tupdates/ready"] error:nil]; // remove old
+	[[NSFileManager defaultManager] removeItemAtPath:[workDir stringByAppendingString:@"tupdates/temp"] error:nil];
 	rmdir([[workDir stringByAppendingString:@"tupdates"] fileSystemRepresentation]);
 }
 
@@ -132,13 +133,19 @@ int main(int argc, const char * argv[]) {
 	}
 
 	if (update) {
-		writeLog([@"Starting update files iteration, path: " stringByAppendingString: [workDir stringByAppendingString:@"tupdates/ready"]]);
-
 		NSFileManager *fileManager = [NSFileManager defaultManager];
-		NSString *srcDir = [workDir stringByAppendingString:@"tupdates/ready/"];
+		NSString *readyFilePath = [workDir stringByAppendingString:@"tupdates/temp/ready"];
+		NSString *srcDir = [workDir stringByAppendingString:@"tupdates/temp/"], *srcEnum = [workDir stringByAppendingString:@"tupdates/temp"];
+		if (![fileManager fileExistsAtPath:readyFilePath]) {
+			srcDir = [workDir stringByAppendingString:@"tupdates/ready/"]; // old
+			srcEnum = [workDir stringByAppendingString:@"tupdates/ready"];
+		}
+
+		writeLog([@"Starting update files iteration, path: " stringByAppendingString: srcEnum]);
+
 		NSArray *keys = [NSArray arrayWithObject:NSURLIsDirectoryKey];
 		NSDirectoryEnumerator *enumerator = [fileManager
-											 enumeratorAtURL:[NSURL fileURLWithPath:[workDir stringByAppendingString:@"tupdates/ready"]]
+											 enumeratorAtURL:[NSURL fileURLWithPath:srcEnum]
 											 includingPropertiesForKeys:keys
 											 options:0
 											 errorHandler:^(NSURL *url, NSError *error) {
@@ -163,18 +170,20 @@ int main(int argc, const char * argv[]) {
 			NSString *dstPath = [appDirFull stringByAppendingString:[pathPart substringFromIndex:r.length]];
 			NSError *error;
 			NSNumber *isDirectory = nil;
-			writeLog([[NSArray arrayWithObjects: @"Copying file ", srcPath, @" to ", dstPath, nil] componentsJoinedByString:@""]);
 			if (![url getResourceValue:&isDirectory forKey:NSURLIsDirectoryKey error:&error]) {
 				writeLog([@"Failed to get IsDirectory for file " stringByAppendingString:[url path]]);
 				delFolder();
 				break;
 			}
 			if ([isDirectory boolValue]) {
+				writeLog([[NSArray arrayWithObjects: @"Copying dir ", srcPath, @" to ", dstPath, nil] componentsJoinedByString:@""]);
 				if (![fileManager createDirectoryAtPath:dstPath withIntermediateDirectories:YES attributes:nil error:nil]) {
 					writeLog([@"Failed to force path for directory " stringByAppendingString:dstPath]);
 					delFolder();
 					break;
 				}
+			} else if ([srcPath isEqualToString:readyFilePath]) {
+				writeLog([[NSArray arrayWithObjects: @"Skipping ready file ", srcPath, nil] componentsJoinedByString:@""]);
 			} else if ([fileManager fileExistsAtPath:dstPath]) {
 				if (![[NSData dataWithContentsOfFile:srcPath] writeToFile:dstPath atomically:YES]) {
 					writeLog([@"Failed to edit file " stringByAppendingString:dstPath]);
diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp
index 1694ccc86b..a59e5e4be6 100644
--- a/Telegram/SourceFiles/application.cpp
+++ b/Telegram/SourceFiles/application.cpp
@@ -29,6 +29,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 
 #include "localstorage.h"
 
+#include "autoupdater.h"
+
 namespace {
 	Application *mainApp = 0;
 	FileUploader *uploader = 0;
@@ -180,29 +182,6 @@ Application::Application(int &argc, char **argv) : PsApplication(argc, argv),
 	}
 }
 
-void Application::onAppUpdate(const MTPhelp_AppUpdate &response) {
-    updateRequestId = 0;
-
-	cSetLastUpdateCheck(unixtime());
-	Local::writeSettings();
-	if (response.type() == mtpc_help_noAppUpdate) {
-		startUpdateCheck();
-	} else {
-		updateThread = new QThread();
-		connect(updateThread, SIGNAL(finished()), updateThread, SLOT(deleteLater()));
-		updateDownloader = new PsUpdateDownloader(updateThread, response.c_help_appUpdate());
-		updateThread->start();
-	}
-}
-
-bool Application::onAppUpdateFail() {
-	updateRequestId = 0;
-	cSetLastUpdateCheck(unixtime());
-	Local::writeSettings();
-	startUpdateCheck();
-	return true;
-}
-
 void Application::updateGotCurrent() {
 	if (!updateReply || updateThread) return;
 
@@ -213,7 +192,7 @@ void Application::updateGotCurrent() {
 		if (currentVersion > AppVersion) {
 			updateThread = new QThread();
 			connect(updateThread, SIGNAL(finished()), updateThread, SLOT(deleteLater()));
-			updateDownloader = new PsUpdateDownloader(updateThread, m.captured(2));
+			updateDownloader = new UpdateDownloader(updateThread, m.captured(2));
 			updateThread->start();
 		}
 	}
@@ -540,7 +519,6 @@ void Application::startUpdateCheck(bool forceWait) {
 		updateReply = updateManager.get(checkVersion);
 		connect(updateReply, SIGNAL(finished()), this, SLOT(updateGotCurrent()));
 		connect(updateReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(updateFailedCurrent(QNetworkReply::NetworkError)));
-//		updateRequestId = MTP::send(MTPhelp_GetAppUpdate(MTP_string(cApiDeviceModel()), MTP_string(cApiSystemVersion()), MTP_string(cApiAppVersion()), MTP_string(cApiLang())), rpcDone(&Application::onAppUpdate), rpcFail(&Application::onAppUpdateFail);
 		emit updateChecking();
 	} else {
 		updateCheckTimer.start((updateInSecs + 5) * 1000);
@@ -648,7 +626,7 @@ void Application::socketError(QLocalSocket::LocalSocketError e) {
 		return App::quit();
 	}
 
-	if (!cNoStartUpdate() && psCheckReadyUpdate()) {
+	if (!cNoStartUpdate() && checkReadyUpdate()) {
 		cSetRestartingUpdate(true);
 		DEBUG_LOG(("Application Info: installing update instead of starting app.."));
 		return App::quit();
diff --git a/Telegram/SourceFiles/application.h b/Telegram/SourceFiles/application.h
index 78d58834df..694db88507 100644
--- a/Telegram/SourceFiles/application.h
+++ b/Telegram/SourceFiles/application.h
@@ -27,6 +27,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 class MainWidget;
 class FileUploader;
 class Translator;
+class UpdateDownloader;
 
 class Application : public PsApplication, public RPCSender {
 	Q_OBJECT
@@ -42,9 +43,6 @@ public:
 	static int32 languageId();
 	static MainWidget *main();
 
-	void onAppUpdate(const MTPhelp_AppUpdate &response);
-	bool onAppUpdateFail();
-
 	enum UpdatingState {
 		UpdatingNone,
 		UpdatingDownload,
@@ -80,6 +78,12 @@ public:
 
 signals:
 
+	void updateChecking();
+	void updateLatest();
+	void updateDownloading(qint64 ready, qint64 total);
+	void updateReady();
+	void updateFailed();
+
 	void peerPhotoDone(PeerId peer);
 	void peerPhotoFail(PeerId peer);
 
@@ -143,7 +147,7 @@ private:
 	QNetworkReply *updateReply;
 	SingleTimer updateCheckTimer;
 	QThread *updateThread;
-	PsUpdateDownloader *updateDownloader;
+	UpdateDownloader *updateDownloader;
 
 	QTimer writeUserConfigTimer;
 
diff --git a/Telegram/SourceFiles/autoupdater.cpp b/Telegram/SourceFiles/autoupdater.cpp
new file mode 100644
index 0000000000..ad0eb6f1af
--- /dev/null
+++ b/Telegram/SourceFiles/autoupdater.cpp
@@ -0,0 +1,520 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It 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 General Public License for more details.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014 John Preston, https://desktop.telegram.org
+*/
+#include "stdafx.h"
+#include "application.h"
+#include "pspecific.h"
+#include "autoupdater.h"
+
+#ifdef Q_OS_WIN
+typedef DWORD VerInt;
+typedef WCHAR VerChar;
+#else
+typedef int VerInt;
+typedef wchar_t VerChar;
+#endif
+
+UpdateDownloader::UpdateDownloader(QThread *thread, const QString &url) : already(0), reply(0), full(0) {
+	updateUrl = url;
+	moveToThread(thread);
+	manager.moveToThread(thread);
+	App::setProxySettings(manager);
+
+	connect(thread, SIGNAL(started()), this, SLOT(start()));
+	initOutput();
+}
+
+void UpdateDownloader::initOutput() {
+	QString fileName;
+	QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
+	if (m.hasMatch()) {
+		fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
+	}
+	if (fileName.isEmpty()) {
+		fileName = qsl("tupdate-%1").arg(rand());
+	}
+	QString dirStr = cWorkingDir() + qsl("tupdates/");
+	fileName = dirStr + fileName;
+	QFileInfo file(fileName);
+
+	QDir dir(dirStr);
+	if (dir.exists()) {
+		QFileInfoList all = dir.entryInfoList(QDir::Files);
+		for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) {
+			if (i->absoluteFilePath() != file.absoluteFilePath()) {
+				QFile::remove(i->absoluteFilePath());
+			}
+		}
+	} else {
+		dir.mkdir(dir.absolutePath());
+	}
+	outputFile.setFileName(fileName);
+	if (file.exists()) {
+		uint64 fullSize = file.size();
+		if (fullSize < INT_MAX) {
+			int32 goodSize = (int32)fullSize;
+			if (goodSize % UpdateChunk) {
+				goodSize = goodSize - (goodSize % UpdateChunk);
+				if (goodSize) {
+					if (outputFile.open(QIODevice::ReadOnly)) {
+						QByteArray goodData = outputFile.readAll().mid(0, goodSize);
+						outputFile.close();
+						if (outputFile.open(QIODevice::WriteOnly)) {
+							outputFile.write(goodData);
+							outputFile.close();
+
+							QMutexLocker lock(&mutex);
+							already = goodSize;
+						}
+					}
+				}
+			} else {
+				QMutexLocker lock(&mutex);
+				already = goodSize;
+			}
+		}
+		if (!already) {
+			QFile::remove(fileName);
+		}
+	}
+}
+
+void UpdateDownloader::start() {
+	sendRequest();
+}
+
+void UpdateDownloader::sendRequest() {
+	QNetworkRequest req(updateUrl);
+	QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";
+	req.setRawHeader("Range", rangeHeaderValue);
+	req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
+	if (reply) reply->deleteLater();
+	reply = manager.get(req);
+	connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64)));
+	connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError)));
+	connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
+}
+
+void UpdateDownloader::partMetaGot() {
+	typedef QList<QNetworkReply::RawHeaderPair> Pairs;
+	Pairs pairs = reply->rawHeaderPairs();
+	for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
+		if (QString::fromUtf8(i->first).toLower() == "content-range") {
+			QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second));
+			if (m.hasMatch()) {
+				{
+					QMutexLocker lock(&mutex);
+					full = m.captured(1).toInt();
+				}
+				emit App::app()->updateDownloading(already, full);
+			}
+		}
+	}
+}
+
+int32 UpdateDownloader::ready() {
+	QMutexLocker lock(&mutex);
+	return already;
+}
+
+int32 UpdateDownloader::size() {
+	QMutexLocker lock(&mutex);
+	return full;
+}
+
+void UpdateDownloader::partFinished(qint64 got, qint64 total) {
+	if (!reply) return;
+
+	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+	if (statusCode.isValid()) {
+		int status = statusCode.toInt();
+		if (status != 200 && status != 206 && status != 416) {
+			LOG(("Update Error: Bad HTTP status received in partFinished(): %1").arg(status));
+			return fatalFail();
+		}
+	}
+
+	if (!already && !full) {
+		QMutexLocker lock(&mutex);
+		full = total;
+	}
+	DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
+
+	if (!outputFile.isOpen()) {
+		if (!outputFile.open(QIODevice::Append)) {
+			LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName()));
+			return fatalFail();
+		}
+	}
+	QByteArray r = reply->readAll();
+	if (!r.isEmpty()) {
+		outputFile.write(r);
+
+		QMutexLocker lock(&mutex);
+		already += r.size();
+	}
+	if (got >= total) {
+		reply->deleteLater();
+		reply = 0;
+		outputFile.close();
+		unpackUpdate();
+	} else {
+		emit App::app()->updateDownloading(already, full);
+	}
+}
+
+void UpdateDownloader::partFailed(QNetworkReply::NetworkError e) {
+	if (!reply) return;
+
+	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
+	reply->deleteLater();
+	reply = 0;
+	if (statusCode.isValid()) {
+		int status = statusCode.toInt();
+		if (status == 416) { // Requested range not satisfiable
+			outputFile.close();
+			unpackUpdate();
+			return;
+		}
+	}
+	LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
+	emit App::app()->updateFailed();
+}
+
+void UpdateDownloader::fatalFail() {
+	clearAll();
+	emit App::app()->updateFailed();
+}
+
+void UpdateDownloader::clearAll() {
+	psDeleteDir(cWorkingDir() + qsl("tupdates"));
+}
+
+//QString winapiErrorWrap() {
+//	WCHAR errMsg[2048];
+//	DWORD errorCode = GetLastError();
+//	LPTSTR errorText = NULL, errorTextDefault = L"(Unknown error)";
+//	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, 0, 0);
+//	if (!errorText) {
+//		errorText = errorTextDefault;
+//	}
+//	StringCbPrintf(errMsg, sizeof(errMsg), L"Error code: %d, error message: %s", errorCode, errorText);
+//	if (errorText != errorTextDefault) {
+//		LocalFree(errorText);
+//	}
+//	return QString::fromWCharArray(errMsg);
+//}
+
+void UpdateDownloader::unpackUpdate() {
+	QByteArray packed;
+	if (!outputFile.open(QIODevice::ReadOnly)) {
+		LOG(("Update Error: cant read updates file!"));
+		return fatalFail();
+	}
+
+#ifdef Q_OS_WIN // use Lzma SDK for win
+	const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
+#else
+	const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
+#endif
+
+	QByteArray compressed = outputFile.readAll();
+	int32 compressedLen = compressed.size() - hSize;
+	if (compressedLen <= 0) {
+		LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
+		return fatalFail();
+	}
+	outputFile.close();
+
+	QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready");
+	psDeleteDir(tempDirPath);
+
+	QDir tempDir(tempDirPath);
+	if (tempDir.exists() || QFile(readyFilePath).exists()) {
+		LOG(("Update Error: cant clear tupdates/temp dir!"));
+		return fatalFail();
+	}
+
+	uchar sha1Buffer[20];
+	bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen);
+	if (!goodSha1) {
+		LOG(("Update Error: bad SHA1 hash of update file!"));
+		return fatalFail();
+	}
+
+	RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(DevChannel ? UpdatesPublicDevKey : UpdatesPublicKey), -1), 0, 0, 0);
+	if (!pbKey) {
+		LOG(("Update Error: cant read public rsa key!"));
+		return fatalFail();
+	}
+	if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
+		RSA_free(pbKey);
+		LOG(("Update Error: bad RSA signature of update file!"));
+		return fatalFail();
+	}
+	RSA_free(pbKey);
+
+	QByteArray uncompressed;
+
+	int32 uncompressedLen;
+	memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
+	uncompressed.resize(uncompressedLen);
+
+	size_t resultLen = uncompressed.size();
+#ifdef Q_OS_WIN // use Lzma SDK for win
+	SizeT srcLen = compressedLen;
+	int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
+	if (uncompressRes != SZ_OK) {
+		LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
+		return fatalFail();
+	}
+#else
+	lzma_stream stream = LZMA_STREAM_INIT;
+
+	lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
+	if (ret != LZMA_OK) {
+		const char *msg;
+		switch (ret) {
+		case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
+		case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
+		case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
+		default: msg = "Unknown error, possibly a bug"; break;
+		}
+		LOG(("Error initializing the decoder: %1 (error code %2)").arg(msg).arg(ret));
+		return fatalFail();
+	}
+
+	stream.avail_in = compressedLen;
+	stream.next_in = (uint8_t*)(compressed.constData() + hSize);
+	stream.avail_out = resultLen;
+	stream.next_out = (uint8_t*)uncompressed.data();
+
+	lzma_ret res = lzma_code(&stream, LZMA_FINISH);
+	if (stream.avail_in) {
+		LOG(("Error in decompression, %1 bytes left in _in of %2 whole.").arg(stream.avail_in).arg(compressedLen));
+		return fatalFail();
+	} else if (stream.avail_out) {
+		LOG(("Error in decompression, %1 bytes free left in _out of %2 whole.").arg(stream.avail_out).arg(resultLen));
+		return fatalFail();
+	}
+	lzma_end(&stream);
+	if (res != LZMA_OK && res != LZMA_STREAM_END) {
+		const char *msg;
+		switch (res) {
+		case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
+		case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break;
+		case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break;
+		case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break;
+		case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break;
+		default: msg = "Unknown error, possibly a bug"; break;
+		}
+		LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res));
+		return fatalFail();
+	}
+#endif
+
+	tempDir.mkdir(tempDir.absolutePath());
+
+	quint32 version;
+	{
+		QBuffer buffer(&uncompressed);
+		buffer.open(QIODevice::ReadOnly);
+		QDataStream stream(&buffer);
+		stream.setVersion(QDataStream::Qt_5_1);
+
+		stream >> version;
+		if (stream.status() != QDataStream::Ok) {
+			LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status()));
+			return fatalFail();
+		}
+		if (int32(version) <= AppVersion) {
+			LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion));
+			return fatalFail();
+		}
+
+		quint32 filesCount;
+		stream >> filesCount;
+		if (stream.status() != QDataStream::Ok) {
+			LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status()));
+			return fatalFail();
+		}
+		if (!filesCount) {
+			LOG(("Update Error: update is empty!"));
+			return fatalFail();
+		}
+		for (uint32 i = 0; i < filesCount; ++i) {
+			QString relativeName;
+			quint32 fileSize;
+			QByteArray fileInnerData;
+			bool executable = false;
+
+			stream >> relativeName >> fileSize >> fileInnerData;
+#if defined Q_OS_MAC || defined Q_OS_LINUX
+			stream >> executable;
+#endif
+			if (stream.status() != QDataStream::Ok) {
+				LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
+				return fatalFail();
+			}
+			if (fileSize != quint32(fileInnerData.size())) {
+				LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size()));
+				return fatalFail();
+			}
+
+			QFile f(tempDirPath + '/' + relativeName);
+			if (!QDir().mkpath(QFileInfo(f).absolutePath())) {
+				LOG(("Update Error: cant mkpath for file '%1'").arg(tempDirPath + '/' + relativeName));
+				return fatalFail();
+			}
+			if (!f.open(QIODevice::WriteOnly)) {
+				LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
+				return fatalFail();
+			}
+			if (f.write(fileInnerData) != fileSize) {
+				f.close();
+				LOG(("Update Error: cant write file '%1'").arg(tempDirPath + '/' + relativeName));
+				return fatalFail();
+			}
+			f.close();
+			if (executable) {
+				QFileDevice::Permissions p = f.permissions();
+				p |= QFileDevice::ExeOwner | QFileDevice::ExeUser | QFileDevice::ExeGroup | QFileDevice::ExeOther;
+				f.setPermissions(p);
+			}
+		}
+
+		// create tdata/version file
+		tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath());
+		std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString();
+
+		VerInt versionNum = VerInt(version), versionLen = VerInt(versionString.size() * sizeof(VerChar));
+		VerChar versionStr[32];
+		memcpy(versionStr, versionString.c_str(), versionLen);
+
+		QFile fVersion(tempDirPath + qsl("/tdata/version"));
+		if (!fVersion.open(QIODevice::WriteOnly)) {
+			LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/version")));
+			return fatalFail();
+		}
+		fVersion.write((const char*)&versionNum, sizeof(VerInt));
+		fVersion.write((const char*)&versionLen, sizeof(VerInt));
+		fVersion.write((const char*)&versionStr[0], versionLen);
+		fVersion.close();
+	}
+
+	QFile readyFile(readyFilePath);
+	if (readyFile.open(QIODevice::WriteOnly)) {
+		if (readyFile.write("1", 1)) {
+			readyFile.close();
+		} else {
+			LOG(("Update Error: cant write ready file '%1'").arg(readyFilePath));
+			return fatalFail();
+		}
+	} else {
+		LOG(("Update Error: cant create ready file '%1'").arg(readyFilePath));
+		return fatalFail();
+	}
+	outputFile.remove();
+
+	emit App::app()->updateReady();
+}
+
+UpdateDownloader::~UpdateDownloader() {
+	delete reply;
+	reply = 0;
+}
+
+bool checkReadyUpdate() {
+	QString readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready"), readyPath = cWorkingDir() + qsl("tupdates/temp");
+	if (!QFile(readyFilePath).exists()) {
+		return false;
+	}
+
+	// check ready version
+	QString versionPath = readyPath + qsl("/tdata/version");
+	{
+		QFile fVersion(versionPath);
+		if (!fVersion.open(QIODevice::ReadOnly)) {
+			LOG(("Update Error: cant read version file '%1'").arg(versionPath));
+			UpdateDownloader::clearAll();
+			return false;
+		}
+		VerInt versionNum;
+		if (fVersion.read((char*)&versionNum, sizeof(VerInt)) != sizeof(VerInt)) {
+			LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
+			UpdateDownloader::clearAll();
+			return false;
+		}
+		fVersion.close();
+		if (versionNum <= AppVersion) {
+			LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
+			UpdateDownloader::clearAll();
+			return false;
+		}
+	}
+
+#ifdef Q_OS_WIN
+	QString curUpdater = (cExeDir() + qsl("Updater.exe"));
+	QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Updater.exe"));
+#elif defined Q_OS_MAC
+	QString curUpdater = (cExeDir() + cExeName() + qsl("/Contents/Frameworks/Updater"));
+	QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Telegram.app/Contents/Frameworks/Updater"));
+#elif defined Q_OS_LINUX
+	QString curUpdater = (cExeDir() + qsl("Updater"));
+	QFileInfo updater(cWorkingDir() + qsl("tupdates/temp/Updater"));
+#endif
+	if (!updater.exists()) {
+		QFileInfo current(curUpdater);
+		if (!current.exists()) {
+			UpdateDownloader::clearAll();
+			return false;
+		}
+		if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
+			UpdateDownloader::clearAll();
+			return false;
+		}
+	}
+#ifdef Q_OS_WIN
+	if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
+		DWORD errorCode = GetLastError();
+		if (errorCode == ERROR_ACCESS_DENIED) { // we are in write-protected dir, like Program Files
+			cSetWriteProtected(true);
+			return true;
+		} else {
+			UpdateDownloader::clearAll();
+			return false;
+		}
+	}
+	if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
+		UpdateDownloader::clearAll();
+		return false;
+	}
+#elif defined Q_OS_MAC
+	QDir().mkpath(QFileInfo(curUpdater).absolutePath());
+	DEBUG_LOG(("Update Info: moving %1 to %2..").arg(updater.absoluteFilePath()).arg(curUpdater));
+	if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
+		UpdateDownloader::clearAll();
+		return false;
+	}
+#elif defined Q_OS_LINUX
+	if (!moveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
+		UpdateDownloader::clearAll();
+		return false;
+	}
+#endif
+	return true;
+}
diff --git a/Telegram/SourceFiles/autoupdater.h b/Telegram/SourceFiles/autoupdater.h
new file mode 100644
index 0000000000..097f335ad2
--- /dev/null
+++ b/Telegram/SourceFiles/autoupdater.h
@@ -0,0 +1,62 @@
+/*
+This file is part of Telegram Desktop,
+the official desktop version of Telegram messaging app, see https://telegram.org
+
+Telegram Desktop is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+It 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 General Public License for more details.
+
+Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
+Copyright (c) 2014 John Preston, https://desktop.telegram.org
+*/
+#pragma once
+
+#include <QtNetwork/QLocalSocket>
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QNetworkReply>
+
+class UpdateDownloader : public QObject {
+	Q_OBJECT
+
+public:
+	UpdateDownloader(QThread *thread, const QString &url);
+
+	void unpackUpdate();
+
+	int32 ready();
+	int32 size();
+
+	static void clearAll();
+
+	~UpdateDownloader();
+
+public slots:
+
+	void start();
+	void partMetaGot();
+	void partFinished(qint64 got, qint64 total);
+	void partFailed(QNetworkReply::NetworkError e);
+	void sendRequest();
+
+private:
+	void initOutput();
+
+	void fatalFail();
+
+	QString updateUrl;
+	QNetworkAccessManager manager;
+	QNetworkReply *reply;
+	int32 already, full;
+	QFile outputFile;
+
+	QMutex mutex;
+
+};
+
+bool checkReadyUpdate();
diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp
index b5bbf30c51..5b9ffef100 100644
--- a/Telegram/SourceFiles/pspecific_linux.cpp
+++ b/Telegram/SourceFiles/pspecific_linux.cpp
@@ -946,183 +946,6 @@ PsApplication::~PsApplication() {
     _psEventFilter = 0;
 }
 
-PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update) : reply(0), already(0), full(0) {
-	updateUrl = qs(update.vurl);
-	moveToThread(thread);
-	manager.moveToThread(thread);
-	App::setProxySettings(manager);
-
-	connect(thread, SIGNAL(started()), this, SLOT(start()));
-	initOutput();
-}
-
-PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const QString &url) : reply(0), already(0), full(0) {
-	updateUrl = url;
-	moveToThread(thread);
-	manager.moveToThread(thread);
-	App::setProxySettings(manager);
-
-	connect(thread, SIGNAL(started()), this, SLOT(start()));
-	initOutput();
-}
-
-void PsUpdateDownloader::initOutput() {
-	QString fileName;
-	QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
-	if (m.hasMatch()) {
-		fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
-	}
-	if (fileName.isEmpty()) {
-		fileName = qsl("tupdate-%1").arg(rand());
-	}
-	QString dirStr = cWorkingDir() + qsl("tupdates/");
-	fileName = dirStr + fileName;
-	QFileInfo file(fileName);
-
-	QDir dir(dirStr);
-	if (dir.exists()) {
-		QFileInfoList all = dir.entryInfoList(QDir::Files);
-		for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) {
-			if (i->absoluteFilePath() != file.absoluteFilePath()) {
-				QFile::remove(i->absoluteFilePath());
-			}
-		}
-	} else {
-		dir.mkdir(dir.absolutePath());
-	}
-	outputFile.setFileName(fileName);
-	if (file.exists()) {
-		uint64 fullSize = file.size();
-		if (fullSize < INT_MAX) {
-			int32 goodSize = (int32)fullSize;
-			if (goodSize % UpdateChunk) {
-				goodSize = goodSize - (goodSize % UpdateChunk);
-				if (goodSize) {
-					if (outputFile.open(QIODevice::ReadOnly)) {
-						QByteArray goodData = outputFile.readAll().mid(0, goodSize);
-						outputFile.close();
-						if (outputFile.open(QIODevice::WriteOnly)) {
-							outputFile.write(goodData);
-							outputFile.close();
-							
-							QMutexLocker lock(&mutex);
-							already = goodSize;
-						}
-					}
-				}
-			} else {
-				QMutexLocker lock(&mutex);
-				already = goodSize;
-			}
-		}
-		if (!already) {
-			QFile::remove(fileName);
-		}
-	}
-}
-
-void PsUpdateDownloader::start() {
-	sendRequest();
-}
-
-void PsUpdateDownloader::sendRequest() {
-	QNetworkRequest req(updateUrl);
-	QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";// + QByteArray::number(already + cUpdateChunk() - 1); 
-	req.setRawHeader("Range", rangeHeaderValue);
-	req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
-	if (reply) reply->deleteLater();
-	reply = manager.get(req);
-	connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64)));
-	connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError)));
-	connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
-}
-
-void PsUpdateDownloader::partMetaGot() {
-	typedef QList<QNetworkReply::RawHeaderPair> Pairs;
-	Pairs pairs = reply->rawHeaderPairs();
-	for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
-		if (QString::fromUtf8(i->first).toLower() == "content-range") {
-			QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second));
-			if (m.hasMatch()) {
-				{
-					QMutexLocker lock(&mutex);
-					full = m.captured(1).toInt();
-				}
-				emit App::app()->updateDownloading(already, full);
-			}
-		}
-	}
-}
-
-int32 PsUpdateDownloader::ready() {
-	QMutexLocker lock(&mutex);
-	return already;
-}
-
-int32 PsUpdateDownloader::size() {
-	QMutexLocker lock(&mutex);
-	return full;
-}
-
-void PsUpdateDownloader::partFinished(qint64 got, qint64 total) {
-	if (!reply) return;
-
-	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
-    if (statusCode.isValid()) {
-	    int status = statusCode.toInt();
-		if (status != 200 && status != 206 && status != 416) {
-			LOG(("Update Error: Bad HTTP status received in partFinished(): %1").arg(status));
-			return fatalFail();
-		}
-	}
-
-	if (!already && !full) {
-		QMutexLocker lock(&mutex);
-		full = total;
-	}
-	DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
-
-	if (!outputFile.isOpen()) {
-		if (!outputFile.open(QIODevice::Append)) {
-			LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName()));
-			return fatalFail();
-		}
-	}
-	QByteArray r = reply->readAll();
-	if (!r.isEmpty()) {
-		outputFile.write(r);
-
-		QMutexLocker lock(&mutex);
-		already += r.size();
-	}
-	if (got >= total) {
-		reply->deleteLater();
-		reply = 0;
-		outputFile.close();
-		unpackUpdate();
-	} else {
-		emit App::app()->updateDownloading(already, full);
-	}
-}
-
-void PsUpdateDownloader::partFailed(QNetworkReply::NetworkError e) {
-	if (!reply) return;
-
-	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
-	reply->deleteLater();
-	reply = 0;
-    if (statusCode.isValid()) {
-	    int status = statusCode.toInt();
-		if (status == 416) { // Requested range not satisfiable
-			outputFile.close();
-			unpackUpdate();
-			return;
-		}
-	}
-	LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
-	emit App::app()->updateFailed();
-}
-
 bool _removeDirectory(const QString &path) { // from http://stackoverflow.com/questions/2256945/removing-a-non-empty-directory-programmatically-in-c-or-c
     QByteArray pathRaw = QFile::encodeName(path);
     DIR *d = opendir(pathRaw.constData());
@@ -1154,236 +977,8 @@ bool _removeDirectory(const QString &path) { // from http://stackoverflow.com/qu
     return !rmdir(pathRaw.constData());
 }
 
-void PsUpdateDownloader::deleteDir(const QString &dir) {
-    _removeDirectory(dir);
-}
-
-void PsUpdateDownloader::fatalFail() {
-	clearAll();
-	emit App::app()->updateFailed();
-}
-
-void PsUpdateDownloader::clearAll() {
-	deleteDir(cWorkingDir() + qsl("tupdates"));
-}
-
-#ifdef Q_OS_WIN
-typedef DWORD VerInt;
-typedef WCHAR VerChar;
-#else
-typedef int VerInt;
-typedef wchar_t VerChar;
-#endif
-
-void PsUpdateDownloader::unpackUpdate() {
-    QByteArray packed;
-	if (!outputFile.open(QIODevice::ReadOnly)) {
-		LOG(("Update Error: cant read updates file!"));
-		return fatalFail();
-	}
-#ifdef Q_OS_WIN // use Lzma SDK for win
-	const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
-#else
-	const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
-#endif
-	QByteArray compressed = outputFile.readAll();
-	int32 compressedLen = compressed.size() - hSize;
-	if (compressedLen <= 0) {
-		LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
-		return fatalFail();
-	}
-	outputFile.close();
-
-	QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyDirPath = cWorkingDir() + qsl("tupdates/ready");
-	deleteDir(tempDirPath);
-	deleteDir(readyDirPath);
-
-	QDir tempDir(tempDirPath), readyDir(readyDirPath);
-	if (tempDir.exists() || readyDir.exists()) {
-		LOG(("Update Error: cant clear tupdates/temp or tupdates/ready dir!"));
-		return fatalFail();
-	}
-
-	uchar sha1Buffer[20];
-	bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen);
-	if (!goodSha1) {
-		LOG(("Update Error: bad SHA1 hash of update file!"));
-		return fatalFail();
-	}
-
-    RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(DevChannel ? UpdatesPublicDevKey : UpdatesPublicKey), -1), 0, 0, 0);
-	if (!pbKey) {
-		LOG(("Update Error: cant read public rsa key!"));
-		return fatalFail();
-	}
-    if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
-		RSA_free(pbKey);
-		LOG(("Update Error: bad RSA signature of update file!"));
-		return fatalFail();
-    }
-	RSA_free(pbKey);
-
-	QByteArray uncompressed;
-
-	int32 uncompressedLen;
-	memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
-	uncompressed.resize(uncompressedLen);
-
-	size_t resultLen = uncompressed.size();
-#ifdef Q_OS_WIN // use Lzma SDK for win
-	SizeT srcLen = compressedLen;
-	int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
-	if (uncompressRes != SZ_OK) {
-		LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
-		return fatalFail();
-	}
-#else
-	lzma_stream stream = LZMA_STREAM_INIT;
-
-	lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
-	if (ret != LZMA_OK) {
-		const char *msg;
-		switch (ret) {
-			case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
-			case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
-			case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
-			default: msg = "Unknown error, possibly a bug"; break;
-		}
-		LOG(("Error initializing the decoder: %1 (error code %2)").arg(msg).arg(ret));
-		return fatalFail();
-	}
-
-	stream.avail_in = compressedLen;
-	stream.next_in = (uint8_t*)(compressed.constData() + hSize);
-	stream.avail_out = resultLen;
-	stream.next_out = (uint8_t*)uncompressed.data();
-
-	lzma_ret res = lzma_code(&stream, LZMA_FINISH);
-	if (stream.avail_in) {
-		LOG(("Error in decompression, %1 bytes left in _in of %2 whole.").arg(stream.avail_in).arg(compressedLen));
-		return fatalFail();
-	} else if (stream.avail_out) {
-		LOG(("Error in decompression, %1 bytes free left in _out of %2 whole.").arg(stream.avail_out).arg(resultLen));
-		return fatalFail();
-	}
-	lzma_end(&stream);
-	if (res != LZMA_OK && res != LZMA_STREAM_END) {
-		const char *msg;
-		switch (res) {
-			case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
-			case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break;
-			case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break;
-			case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break;
-			case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break;
-			default: msg = "Unknown error, possibly a bug"; break;
-		}
-		LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res));
-		return fatalFail();
-	}
-#endif
-
-	tempDir.mkdir(tempDir.absolutePath());
-
-	quint32 version;
-	{
-		QBuffer buffer(&uncompressed);
-		buffer.open(QIODevice::ReadOnly);
-		QDataStream stream(&buffer);
-		stream.setVersion(QDataStream::Qt_5_1);
-
-		stream >> version;
-		if (stream.status() != QDataStream::Ok) {
-			LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status()));
-			return fatalFail();
-		}
-        if (int32(version) <= AppVersion) {
-			LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion));
-			return fatalFail();
-		}
-
-		quint32 filesCount;
-		stream >> filesCount;
-		if (stream.status() != QDataStream::Ok) {
-			LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status()));
-			return fatalFail();
-		}
-		if (!filesCount) {
-			LOG(("Update Error: update is empty!"));
-			return fatalFail();
-		}
-		for (uint32 i = 0; i < filesCount; ++i) {
-			QString relativeName;
-			quint32 fileSize;
-			QByteArray fileInnerData;
-			bool executable = false;
-
-			stream >> relativeName >> fileSize >> fileInnerData;
-#if defined Q_OS_MAC || defined Q_OS_LINUX
-			stream >> executable;
-#endif
-			if (stream.status() != QDataStream::Ok) {
-				LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
-				return fatalFail();
-			}
-			if (fileSize != quint32(fileInnerData.size())) {
-				LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size()));
-				return fatalFail();
-			}
-
-			QFile f(tempDirPath + '/' + relativeName);
-			if (!QDir().mkpath(QFileInfo(f).absolutePath())) {
-				LOG(("Update Error: cant mkpath for file '%1'").arg(tempDirPath + '/' + relativeName));
-				return fatalFail();
-			}
-			if (!f.open(QIODevice::WriteOnly)) {
-				LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
-				return fatalFail();
-			}
-			if (f.write(fileInnerData) != fileSize) {
-				f.close();
-				LOG(("Update Error: cant write file '%1'").arg(tempDirPath + '/' + relativeName));
-				return fatalFail();
-			}
-			f.close();
-			if (executable) {
-				QFileDevice::Permissions p = f.permissions();
-				p |= QFileDevice::ExeOwner | QFileDevice::ExeUser | QFileDevice::ExeGroup | QFileDevice::ExeOther;
-				f.setPermissions(p);
-			}
-		}
-
-		// create tdata/version file
-		tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath());
-		std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString();
-
-		VerInt versionNum = VerInt(version), versionLen = VerInt(versionString.size() * sizeof(VerChar));
-		VerChar versionStr[32];
-		memcpy(versionStr, versionString.c_str(), versionLen);
-
-		QFile fVersion(tempDirPath + qsl("/tdata/version"));		
-		if (!fVersion.open(QIODevice::WriteOnly)) {
-			LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/version")));
-			return fatalFail();
-		}
-		fVersion.write((const char*)&versionNum, sizeof(VerInt));
-		fVersion.write((const char*)&versionLen, sizeof(VerInt));
-		fVersion.write((const char*)&versionStr[0], versionLen);
-		fVersion.close();
-	}
-	
-	if (!tempDir.rename(tempDir.absolutePath(), readyDir.absolutePath())) {
-		LOG(("Update Error: cant rename temp dir '%1' to ready dir '%2'").arg(tempDir.absolutePath()).arg(readyDir.absolutePath()));
-		return fatalFail();
-	}
-	deleteDir(tempDirPath);
-	outputFile.remove();
-
-    emit App::app()->updateReady();
-}
-
-PsUpdateDownloader::~PsUpdateDownloader() {
-	delete reply;
-	reply = 0;
+void psDeleteDir(const QString &dir) {
+	_removeDirectory(dir);
 }
 
 namespace {
@@ -1497,129 +1092,6 @@ int psFixPrevious() {
 	return 0;
 }
 
-#ifdef Q_OS_LINUX
-bool moveFile(const char *from, const char *to) {
-    FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
-    if (!ffrom) {
-        if (fto) fclose(fto);
-        return false;
-    }
-    if (!fto) {
-        fclose(ffrom);
-        return false;
-    }
-    static const int BufSize = 65536;
-    char buf[BufSize];
-    while (size_t size = fread(buf, 1, BufSize, ffrom)) {
-        fwrite(buf, 1, size, fto);
-    }
-
-    struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
-    //let's say this wont fail since you already worked OK on that fp
-    if (fstat(fileno(ffrom), &fst) != 0) {
-        fclose(ffrom);
-        fclose(fto);
-        return false;
-    }
-    //update to the same uid/gid
-    if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
-        fclose(ffrom);
-        fclose(fto);
-        return false;
-    }
-    //update the permissions
-    if (fchmod(fileno(fto), fst.st_mode) != 0) {
-        fclose(ffrom);
-        fclose(fto);
-        return false;
-    }
-
-    fclose(ffrom);
-    fclose(fto);
-
-    if (unlink(from)) {
-        return false;
-    }
-
-    return true;
-}
-#endif
-
-bool psCheckReadyUpdate() {
-    QString readyPath = cWorkingDir() + qsl("tupdates/ready");
-	if (!QDir(readyPath).exists()) {
-		return false;
-	}
-
-	// check ready version
-	QString versionPath = readyPath + qsl("/tdata/version");
-	{
-		QFile fVersion(versionPath);
-		if (!fVersion.open(QIODevice::ReadOnly)) {
-			LOG(("Update Error: cant read version file '%1'").arg(versionPath));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		VerInt versionNum;
-		if (fVersion.read((char*)&versionNum, sizeof(VerInt)) != sizeof(VerInt)) {
-			LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		fVersion.close();
-		if (versionNum <= AppVersion) {
-			LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-	}
-
-#ifdef Q_OS_WIN
-	QString curUpdater = (cExeDir() + "Updater.exe");
-	QFileInfo updater(cWorkingDir() + "tupdates/ready/Updater.exe");
-#elif defined Q_OS_MAC
-	QString curUpdater = (cExeDir() + "Telegram.app/Contents/Frameworks/Updater");
-	QFileInfo updater(cWorkingDir() + "tupdates/ready/Telegram.app/Contents/Frameworks/Updater");
-#elif defined Q_OS_LINUX
-    QString curUpdater = (cExeDir() + "Updater");
-    QFileInfo updater(cWorkingDir() + "tupdates/ready/Updater");
-#endif
-	if (!updater.exists()) {
-		QFileInfo current(curUpdater);
-		if (!current.exists()) {
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-	}
-#ifdef Q_OS_WIN
-	if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
-		PsUpdateDownloader::clearAll();
-		return false;
-	}
-	if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
-		PsUpdateDownloader::clearAll();
-		return false;
-    }
-#elif defined Q_OS_MAC
-	QFileInfo to(curUpdater);
-	QDir().mkpath(to.absolutePath());
-	if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
-		PsUpdateDownloader::clearAll();
-		return false;
-	}
-#elif defined Q_OS_LINUX
-    if (!moveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
-        PsUpdateDownloader::clearAll();
-        return false;
-    }
-#endif
-    return true;
-}
-
 void psPostprocessFile(const QString &name) {
 }
 
@@ -1781,8 +1253,7 @@ bool _execUpdater(bool update = true) {
 
 void psExecUpdater() {
     if (!_execUpdater()) {
-		QString readyPath = cWorkingDir() + qsl("tupdates/ready");
-		PsUpdateDownloader::deleteDir(readyPath);
+		psDeleteDir(cWorkingDir() + qsl("tupdates/temp"));
 	}
 }
 
@@ -1802,3 +1273,49 @@ void psSendToMenu(bool send, bool silent) {
 
 void psUpdateOverlayed(QWidget *widget) {
 }
+
+bool linuxMoveFile(const char *from, const char *to) {
+	FILE *ffrom = fopen(from, "rb"), *fto = fopen(to, "wb");
+	if (!ffrom) {
+		if (fto) fclose(fto);
+		return false;
+	}
+	if (!fto) {
+		fclose(ffrom);
+		return false;
+	}
+	static const int BufSize = 65536;
+	char buf[BufSize];
+	while (size_t size = fread(buf, 1, BufSize, ffrom)) {
+		fwrite(buf, 1, size, fto);
+	}
+
+	struct stat fst; // from http://stackoverflow.com/questions/5486774/keeping-fileowner-and-permissions-after-copying-file-in-c
+	//let's say this wont fail since you already worked OK on that fp
+	if (fstat(fileno(ffrom), &fst) != 0) {
+		fclose(ffrom);
+		fclose(fto);
+		return false;
+	}
+	//update to the same uid/gid
+	if (fchown(fileno(fto), fst.st_uid, fst.st_gid) != 0) {
+		fclose(ffrom);
+		fclose(fto);
+		return false;
+	}
+	//update the permissions
+	if (fchmod(fileno(fto), fst.st_mode) != 0) {
+		fclose(ffrom);
+		fclose(fto);
+		return false;
+	}
+
+	fclose(ffrom);
+	fclose(fto);
+
+	if (unlink(from)) {
+		return false;
+	}
+
+	return true;
+}
diff --git a/Telegram/SourceFiles/pspecific_linux.h b/Telegram/SourceFiles/pspecific_linux.h
index d34bab2eaf..580fefcec4 100644
--- a/Telegram/SourceFiles/pspecific_linux.h
+++ b/Telegram/SourceFiles/pspecific_linux.h
@@ -116,55 +116,9 @@ public:
 	void psInstallEventFilter();
 	~PsApplication();
 
-signals:
-
-	void updateChecking();
-	void updateLatest();
-	void updateDownloading(qint64 ready, qint64 total);
-	void updateReady();
-	void updateFailed();
-
 };
 
-class PsUpdateDownloader : public QObject {
-	Q_OBJECT
-
-public:
-	PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update);
-	PsUpdateDownloader(QThread *thread, const QString &url);
-
-	void unpackUpdate();
-
-	int32 ready();
-	int32 size();
-
-	static void deleteDir(const QString &dir);
-	static void clearAll();
-
-	~PsUpdateDownloader();
-
-public slots:
-
-	void start();
-	void partMetaGot();
-	void partFinished(qint64 got, qint64 total);
-	void partFailed(QNetworkReply::NetworkError e);
-	void sendRequest();
-
-private:
-	void initOutput();
-
-	void fatalFail();
-
-	QString updateUrl;
-	QNetworkAccessManager manager;
-	QNetworkReply *reply;
-	int32 already, full;
-	QFile outputFile;
-
-	QMutex mutex;
-
-};
+void psDeleteDir(const QString &dir);
 
 void psUserActionDone();
 bool psIdleSupported();
@@ -194,7 +148,6 @@ void psBringToBack(QWidget *w);
 int psCleanup();
 int psFixPrevious();
 
-bool psCheckReadyUpdate();
 void psExecUpdater();
 void psExecTelegram();
 
@@ -212,3 +165,5 @@ void psUpdateOverlayed(QWidget *widget);
 inline QString psConvertFileUrl(const QString &url) {
 	return url;
 }
+
+bool linuxMoveFile(const char *from, const char *to);
diff --git a/Telegram/SourceFiles/pspecific_mac.cpp b/Telegram/SourceFiles/pspecific_mac.cpp
index ed3d8c71d1..f957384e28 100644
--- a/Telegram/SourceFiles/pspecific_mac.cpp
+++ b/Telegram/SourceFiles/pspecific_mac.cpp
@@ -522,415 +522,10 @@ PsApplication::~PsApplication() {
     _psEventFilter = 0;
 }
 
-PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update) : reply(0), already(0), full(0) {
-	updateUrl = qs(update.vurl);
-	moveToThread(thread);
-	manager.moveToThread(thread);
-	App::setProxySettings(manager);
-
-	connect(thread, SIGNAL(started()), this, SLOT(start()));
-	initOutput();
-}
-
-PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const QString &url) : reply(0), already(0), full(0) {
-	updateUrl = url;
-	moveToThread(thread);
-	manager.moveToThread(thread);
-	App::setProxySettings(manager);
-
-	connect(thread, SIGNAL(started()), this, SLOT(start()));
-	initOutput();
-}
-
-void PsUpdateDownloader::initOutput() {
-	QString fileName;
-	QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
-	if (m.hasMatch()) {
-		fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
-	}
-	if (fileName.isEmpty()) {
-		fileName = qsl("tupdate-%1").arg(rand());
-	}
-	QString dirStr = cWorkingDir() + qsl("tupdates/");
-	fileName = dirStr + fileName;
-	QFileInfo file(fileName);
-
-	QDir dir(dirStr);
-	if (dir.exists()) {
-		QFileInfoList all = dir.entryInfoList(QDir::Files);
-		for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) {
-			if (i->absoluteFilePath() != file.absoluteFilePath()) {
-				QFile::remove(i->absoluteFilePath());
-			}
-		}
-	} else {
-		dir.mkdir(dir.absolutePath());
-	}
-	outputFile.setFileName(fileName);
-	if (file.exists()) {
-		uint64 fullSize = file.size();
-		if (fullSize < INT_MAX) {
-			int32 goodSize = (int32)fullSize;
-			if (goodSize % UpdateChunk) {
-				goodSize = goodSize - (goodSize % UpdateChunk);
-				if (goodSize) {
-					if (outputFile.open(QIODevice::ReadOnly)) {
-						QByteArray goodData = outputFile.readAll().mid(0, goodSize);
-						outputFile.close();
-						if (outputFile.open(QIODevice::WriteOnly)) {
-							outputFile.write(goodData);
-							outputFile.close();
-							
-							QMutexLocker lock(&mutex);
-							already = goodSize;
-						}
-					}
-				}
-			} else {
-				QMutexLocker lock(&mutex);
-				already = goodSize;
-			}
-		}
-		if (!already) {
-			QFile::remove(fileName);
-		}
-	}
-}
-
-void PsUpdateDownloader::start() {
-	sendRequest();
-}
-
-void PsUpdateDownloader::sendRequest() {
-	QNetworkRequest req(updateUrl);
-	QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";// + QByteArray::number(already + cUpdateChunk() - 1); 
-	req.setRawHeader("Range", rangeHeaderValue);
-	req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
-	if (reply) reply->deleteLater();
-	reply = manager.get(req);
-	connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64)));
-	connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError)));
-	connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
-}
-
-void PsUpdateDownloader::partMetaGot() {
-	typedef QList<QNetworkReply::RawHeaderPair> Pairs;
-	Pairs pairs = reply->rawHeaderPairs();
-	for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
-		if (QString::fromUtf8(i->first).toLower() == "content-range") {
-			QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second));
-			if (m.hasMatch()) {
-				{
-					QMutexLocker lock(&mutex);
-					full = m.captured(1).toInt();
-				}
-				emit App::app()->updateDownloading(already, full);
-			}
-		}
-	}
-}
-
-int32 PsUpdateDownloader::ready() {
-	QMutexLocker lock(&mutex);
-	return already;
-}
-
-int32 PsUpdateDownloader::size() {
-	QMutexLocker lock(&mutex);
-	return full;
-}
-
-void PsUpdateDownloader::partFinished(qint64 got, qint64 total) {
-	if (!reply) return;
-
-	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
-    if (statusCode.isValid()) {
-	    int status = statusCode.toInt();
-		if (status != 200 && status != 206 && status != 416) {
-			LOG(("Update Error: Bad HTTP status received in partFinished(): %1").arg(status));
-			return fatalFail();
-		}
-	}
-
-	if (!already && !full) {
-		QMutexLocker lock(&mutex);
-		full = total;
-	}
-	DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
-
-	if (!outputFile.isOpen()) {
-		if (!outputFile.open(QIODevice::Append)) {
-			LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName()));
-			return fatalFail();
-		}
-	}
-	QByteArray r = reply->readAll();
-	if (!r.isEmpty()) {
-		outputFile.write(r);
-
-		QMutexLocker lock(&mutex);
-		already += r.size();
-	}
-	if (got >= total) {
-		reply->deleteLater();
-		reply = 0;
-		outputFile.close();
-		unpackUpdate();
-	} else {
-		emit App::app()->updateDownloading(already, full);
-	}
-}
-
-void PsUpdateDownloader::partFailed(QNetworkReply::NetworkError e) {
-	if (!reply) return;
-
-	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
-	reply->deleteLater();
-	reply = 0;
-    if (statusCode.isValid()) {
-	    int status = statusCode.toInt();
-		if (status == 416) { // Requested range not satisfiable
-			outputFile.close();
-			unpackUpdate();
-			return;
-		}
-	}
-	LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
-	emit App::app()->updateFailed();
-}
-
-void PsUpdateDownloader::deleteDir(const QString &dir) {
+void psDeleteDir(const QString &dir) {
 	objc_deleteDir(dir);
 }
 
-void PsUpdateDownloader::fatalFail() {
-	clearAll();
-	emit App::app()->updateFailed();
-}
-
-void PsUpdateDownloader::clearAll() {
-	deleteDir(cWorkingDir() + qsl("tupdates"));
-}
-
-#ifdef Q_OS_WIN
-typedef DWORD VerInt;
-typedef WCHAR VerChar;
-#else
-typedef int VerInt;
-typedef wchar_t VerChar;
-#endif
-
-void PsUpdateDownloader::unpackUpdate() {
-    QByteArray packed;
-	if (!outputFile.open(QIODevice::ReadOnly)) {
-		LOG(("Update Error: cant read updates file!"));
-		return fatalFail();
-	}
-#ifdef Q_OS_WIN // use Lzma SDK for win
-	const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
-#else
-	const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
-#endif
-	QByteArray compressed = outputFile.readAll();
-	int32 compressedLen = compressed.size() - hSize;
-	if (compressedLen <= 0) {
-		LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
-		return fatalFail();
-	}
-	outputFile.close();
-
-	QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyDirPath = cWorkingDir() + qsl("tupdates/ready");
-	deleteDir(tempDirPath);
-	deleteDir(readyDirPath);
-
-	QDir tempDir(tempDirPath), readyDir(readyDirPath);
-	if (tempDir.exists() || readyDir.exists()) {
-		LOG(("Update Error: cant clear tupdates/temp or tupdates/ready dir!"));
-		return fatalFail();
-	}
-
-	uchar sha1Buffer[20];
-	bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen);
-	if (!goodSha1) {
-		LOG(("Update Error: bad SHA1 hash of update file!"));
-		return fatalFail();
-	}
-
-	RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(DevChannel ? UpdatesPublicDevKey : UpdatesPublicKey), -1), 0, 0, 0);
-	if (!pbKey) {
-		LOG(("Update Error: cant read public rsa key!"));
-		return fatalFail();
-	}
-    if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
-		RSA_free(pbKey);
-		LOG(("Update Error: bad RSA signature of update file!"));
-		return fatalFail();
-    }
-	RSA_free(pbKey);
-
-	QByteArray uncompressed;
-
-	int32 uncompressedLen;
-	memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
-	uncompressed.resize(uncompressedLen);
-
-	size_t resultLen = uncompressed.size();
-#ifdef Q_OS_WIN // use Lzma SDK for win
-	SizeT srcLen = compressedLen;
-	int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
-	if (uncompressRes != SZ_OK) {
-		LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
-		return fatalFail();
-	}
-#else
-	lzma_stream stream = LZMA_STREAM_INIT;
-
-	lzma_ret ret = lzma_stream_decoder(&stream, UINT64_MAX, LZMA_CONCATENATED);
-	if (ret != LZMA_OK) {
-		const char *msg;
-		switch (ret) {
-			case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
-			case LZMA_OPTIONS_ERROR: msg = "Specified preset is not supported"; break;
-			case LZMA_UNSUPPORTED_CHECK: msg = "Specified integrity check is not supported"; break;
-			default: msg = "Unknown error, possibly a bug"; break;
-		}
-		LOG(("Error initializing the decoder: %1 (error code %2)").arg(msg).arg(ret));
-		return fatalFail();
-	}
-
-	stream.avail_in = compressedLen;
-	stream.next_in = (uint8_t*)(compressed.constData() + hSize);
-	stream.avail_out = resultLen;
-	stream.next_out = (uint8_t*)uncompressed.data();
-
-	lzma_ret res = lzma_code(&stream, LZMA_FINISH);
-	if (stream.avail_in) {
-		LOG(("Error in decompression, %1 bytes left in _in of %2 whole.").arg(stream.avail_in).arg(compressedLen));
-		return fatalFail();
-	} else if (stream.avail_out) {
-		LOG(("Error in decompression, %1 bytes free left in _out of %2 whole.").arg(stream.avail_out).arg(resultLen));
-		return fatalFail();
-	}
-	lzma_end(&stream);
-	if (res != LZMA_OK && res != LZMA_STREAM_END) {
-		const char *msg;
-		switch (res) {
-			case LZMA_MEM_ERROR: msg = "Memory allocation failed"; break;
-			case LZMA_FORMAT_ERROR: msg = "The input data is not in the .xz format"; break;
-			case LZMA_OPTIONS_ERROR: msg = "Unsupported compression options"; break;
-			case LZMA_DATA_ERROR: msg = "Compressed file is corrupt"; break;
-			case LZMA_BUF_ERROR: msg = "Compressed data is truncated or otherwise corrupt"; break;
-			default: msg = "Unknown error, possibly a bug"; break;
-		}
-		LOG(("Error in decompression: %1 (error code %2)").arg(msg).arg(res));
-		return fatalFail();
-	}
-#endif
-
-	tempDir.mkdir(tempDir.absolutePath());
-
-	quint32 version;
-	{
-		QBuffer buffer(&uncompressed);
-		buffer.open(QIODevice::ReadOnly);
-		QDataStream stream(&buffer);
-		stream.setVersion(QDataStream::Qt_5_1);
-
-		stream >> version;
-		if (stream.status() != QDataStream::Ok) {
-			LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status()));
-			return fatalFail();
-		}
-		if (version <= AppVersion) {
-			LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion));
-			return fatalFail();
-		}
-
-		quint32 filesCount;
-		stream >> filesCount;
-		if (stream.status() != QDataStream::Ok) {
-			LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status()));
-			return fatalFail();
-		}
-		if (!filesCount) {
-			LOG(("Update Error: update is empty!"));
-			return fatalFail();
-		}
-		for (uint32 i = 0; i < filesCount; ++i) {
-			QString relativeName;
-			quint32 fileSize;
-			QByteArray fileInnerData;
-			bool executable = false;
-
-			stream >> relativeName >> fileSize >> fileInnerData;
-#if defined Q_OS_MAC || defined Q_OS_LINUX
-			stream >> executable;
-#endif
-			if (stream.status() != QDataStream::Ok) {
-				LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
-				return fatalFail();
-			}
-			if (fileSize != quint32(fileInnerData.size())) {
-				LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size()));
-				return fatalFail();
-			}
-
-			QFile f(tempDirPath + '/' + relativeName);
-			if (!QDir().mkpath(QFileInfo(f).absolutePath())) {
-				LOG(("Update Error: cant mkpath for file '%1'").arg(tempDirPath + '/' + relativeName));
-				return fatalFail();
-			}
-			if (!f.open(QIODevice::WriteOnly)) {
-				LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
-				return fatalFail();
-			}
-			if (f.write(fileInnerData) != fileSize) {
-				f.close();
-				LOG(("Update Error: cant write file '%1'").arg(tempDirPath + '/' + relativeName));
-				return fatalFail();
-			}
-			f.close();
-			if (executable) {
-				QFileDevice::Permissions p = f.permissions();
-				p |= QFileDevice::ExeOwner | QFileDevice::ExeUser | QFileDevice::ExeGroup | QFileDevice::ExeOther;
-				f.setPermissions(p);
-			}
-		}
-
-		// create tdata/version file
-		tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath());
-		std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString();
-
-		VerInt versionNum = VerInt(version), versionLen = VerInt(versionString.size() * sizeof(VerChar));
-		VerChar versionStr[32];
-		memcpy(versionStr, versionString.c_str(), versionLen);
-
-		QFile fVersion(tempDirPath + qsl("/tdata/version"));		
-		if (!fVersion.open(QIODevice::WriteOnly)) {
-			LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/version")));
-			return fatalFail();
-		}
-		fVersion.write((const char*)&versionNum, sizeof(VerInt));
-		fVersion.write((const char*)&versionLen, sizeof(VerInt));
-		fVersion.write((const char*)&versionStr[0], versionLen);
-		fVersion.close();
-	}
-	
-	if (!tempDir.rename(tempDir.absolutePath(), readyDir.absolutePath())) {
-		LOG(("Update Error: cant rename temp dir '%1' to ready dir '%2'").arg(tempDir.absolutePath()).arg(readyDir.absolutePath()));
-		return fatalFail();
-	}
-	deleteDir(tempDirPath);
-	outputFile.remove();
-
-    emit App::app()->updateReady();
-}
-
-PsUpdateDownloader::~PsUpdateDownloader() {
-	delete reply;
-	reply = 0;
-}
-
 namespace {
 	uint64 _lastUserAction = 0;
 }
@@ -1029,73 +624,6 @@ int psFixPrevious() {
 	return 0;
 }
 
-bool psCheckReadyUpdate() {
-    QString readyPath = cWorkingDir() + qsl("tupdates/ready");
-	if (!QDir(readyPath).exists()) {
-		return false;
-	}
-
-	// check ready version
-	QString versionPath = readyPath + qsl("/tdata/version");
-	{
-		QFile fVersion(versionPath);
-		if (!fVersion.open(QIODevice::ReadOnly)) {
-			LOG(("Update Error: cant read version file '%1'").arg(versionPath));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		VerInt versionNum;
-		if (fVersion.read((char*)&versionNum, sizeof(VerInt)) != sizeof(VerInt)) {
-			LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		fVersion.close();
-		if (versionNum <= AppVersion) {
-			LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-	}
-
-#ifdef Q_OS_WIN
-	QString curUpdater = (cExeDir() + qsl("Updater.exe"));
-	QFileInfo updater(cWorkingDir() + qsl("tupdates/ready/Updater.exe"));
-#elif defined Q_OS_MAC
-	QString curUpdater = (cExeDir() + cExeName() + qsl("/Contents/Frameworks/Updater"));
-	QFileInfo updater(cWorkingDir() + qsl("tupdates/ready/Telegram.app/Contents/Frameworks/Updater"));
-#endif
-	if (!updater.exists()) {
-		QFileInfo current(curUpdater);
-		if (!current.exists()) {
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-	}
-#ifdef Q_OS_WIN
-	if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
-		PsUpdateDownloader::clearAll();
-		return false;
-	}
-	if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
-		PsUpdateDownloader::clearAll();
-		return false;
-    }
-#elif defined Q_OS_MAC
-	QDir().mkpath(QFileInfo(curUpdater).absolutePath());
-	DEBUG_LOG(("Update Info: moving %1 to %2..").arg(updater.absoluteFilePath()).arg(curUpdater));
-	if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
-		PsUpdateDownloader::clearAll();
-		return false;
-	}
-#endif
-    return true;
-}
-
 bool psShowOpenWithMenu(int x, int y, const QString &file) {
 	return objc_showOpenWithMenu(x, y, file);
 }
@@ -1125,8 +653,7 @@ void psRegisterCustomScheme() {
 
 void psExecUpdater() {
 	if (!objc_execUpdater()) {
-		QString readyPath = cWorkingDir() + qsl("tupdates/ready");
-		PsUpdateDownloader::deleteDir(readyPath);
+		psDeleteDir(cWorkingDir() + qsl("tupdates/temp"));
 	}
 }
 
diff --git a/Telegram/SourceFiles/pspecific_mac.h b/Telegram/SourceFiles/pspecific_mac.h
index 448930ca3e..ffca8718af 100644
--- a/Telegram/SourceFiles/pspecific_mac.h
+++ b/Telegram/SourceFiles/pspecific_mac.h
@@ -144,55 +144,9 @@ public:
 	void psInstallEventFilter();
 	~PsApplication();
 
-signals:
-
-	void updateChecking();
-	void updateLatest();
-	void updateDownloading(qint64 ready, qint64 total);
-	void updateReady();
-	void updateFailed();
-
 };
 
-class PsUpdateDownloader : public QObject {
-	Q_OBJECT
-
-public:
-	PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update);
-	PsUpdateDownloader(QThread *thread, const QString &url);
-
-	void unpackUpdate();
-
-	int32 ready();
-	int32 size();
-
-	static void deleteDir(const QString &dir);
-	static void clearAll();
-
-	~PsUpdateDownloader();
-
-public slots:
-
-	void start();
-	void partMetaGot();
-	void partFinished(qint64 got, qint64 total);
-	void partFailed(QNetworkReply::NetworkError e);
-	void sendRequest();
-
-private:
-	void initOutput();
-
-	void fatalFail();
-
-	QString updateUrl;
-	QNetworkAccessManager manager;
-	QNetworkReply *reply;
-	int32 already, full;
-	QFile outputFile;
-
-	QMutex mutex;
-
-};
+void psDeleteDir(const QString &dir);
 
 void psUserActionDone();
 bool psIdleSupported();
@@ -222,7 +176,6 @@ void psBringToBack(QWidget *w);
 int psCleanup();
 int psFixPrevious();
 
-bool psCheckReadyUpdate();
 void psExecUpdater();
 void psExecTelegram();
 
diff --git a/Telegram/SourceFiles/pspecific_wnd.cpp b/Telegram/SourceFiles/pspecific_wnd.cpp
index abebf74202..15ab2bf9d5 100644
--- a/Telegram/SourceFiles/pspecific_wnd.cpp
+++ b/Telegram/SourceFiles/pspecific_wnd.cpp
@@ -1385,184 +1385,7 @@ PsApplication::~PsApplication() {
 	_psEventFilter = 0;
 }
 
-PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update) : already(0), reply(0), full(0) {
-	updateUrl = qs(update.vurl);
-	moveToThread(thread);
-	manager.moveToThread(thread);
-	App::setProxySettings(manager);
-
-	connect(thread, SIGNAL(started()), this, SLOT(start()));
-	initOutput();
-}
-
-PsUpdateDownloader::PsUpdateDownloader(QThread *thread, const QString &url) : already(0), reply(0), full(0) {
-	updateUrl = url;
-	moveToThread(thread);
-	manager.moveToThread(thread);
-	App::setProxySettings(manager);
-
-	connect(thread, SIGNAL(started()), this, SLOT(start()));
-	initOutput();
-}
-
-void PsUpdateDownloader::initOutput() {
-	QString fileName;
-	QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
-	if (m.hasMatch()) {
-		fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
-	}
-	if (fileName.isEmpty()) {
-		fileName = qsl("tupdate-%1").arg(rand());
-	}
-	QString dirStr = cWorkingDir() + qsl("tupdates/");
-	fileName = dirStr + fileName;
-	QFileInfo file(fileName);
-
-	QDir dir(dirStr);
-	if (dir.exists()) {
-		QFileInfoList all = dir.entryInfoList(QDir::Files);
-		for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) {
-			if (i->absoluteFilePath() != file.absoluteFilePath()) {
-				QFile::remove(i->absoluteFilePath());
-			}
-		}
-	} else {
-		dir.mkdir(dir.absolutePath());
-	}
-	outputFile.setFileName(fileName);
-	if (file.exists()) {
-		uint64 fullSize = file.size();
-		if (fullSize < INT_MAX) {
-			int32 goodSize = (int32)fullSize;
-			if (goodSize % UpdateChunk) {
-				goodSize = goodSize - (goodSize % UpdateChunk);
-				if (goodSize) {
-					if (outputFile.open(QIODevice::ReadOnly)) {
-						QByteArray goodData = outputFile.readAll().mid(0, goodSize);
-						outputFile.close();
-						if (outputFile.open(QIODevice::WriteOnly)) {
-							outputFile.write(goodData);
-							outputFile.close();
-							
-							QMutexLocker lock(&mutex);
-							already = goodSize;
-						}
-					}
-				}
-			} else {
-				QMutexLocker lock(&mutex);
-				already = goodSize;
-			}
-		}
-		if (!already) {
-			QFile::remove(fileName);
-		}
-	}
-}
-
-void PsUpdateDownloader::start() {
-	sendRequest();
-}
-
-void PsUpdateDownloader::sendRequest() {
-	QNetworkRequest req(updateUrl);
-	QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";// + QByteArray::number(already + cUpdateChunk() - 1); 
-	req.setRawHeader("Range", rangeHeaderValue);
-	req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
-	if (reply) reply->deleteLater();
-	reply = manager.get(req);
-	connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64)));
-	connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError)));
-	connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
-}
-
-void PsUpdateDownloader::partMetaGot() {
-	typedef QList<QNetworkReply::RawHeaderPair> Pairs;
-	Pairs pairs = reply->rawHeaderPairs();
-	for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
-		if (QString::fromUtf8(i->first).toLower() == "content-range") {
-			QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second));
-			if (m.hasMatch()) {
-				{
-					QMutexLocker lock(&mutex);
-					full = m.captured(1).toInt();
-				}
-				emit App::app()->updateDownloading(already, full);
-			}
-		}
-	}
-}
-
-int32 PsUpdateDownloader::ready() {
-	QMutexLocker lock(&mutex);
-	return already;
-}
-
-int32 PsUpdateDownloader::size() {
-	QMutexLocker lock(&mutex);
-	return full;
-}
-
-void PsUpdateDownloader::partFinished(qint64 got, qint64 total) {
-	if (!reply) return;
-
-	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
-    if (statusCode.isValid()) {
-	    int status = statusCode.toInt();
-		if (status != 200 && status != 206 && status != 416) {
-			LOG(("Update Error: Bad HTTP status received in partFinished(): %1").arg(status));
-			return fatalFail();
-		}
-	}
-
-	if (!already && !full) {
-		QMutexLocker lock(&mutex);
-		full = total;
-	}
-	DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
-
-	if (!outputFile.isOpen()) {
-		if (!outputFile.open(QIODevice::Append)) {
-			LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName()));
-			return fatalFail();
-		}
-	}
-	QByteArray r = reply->readAll();
-	if (!r.isEmpty()) {
-		outputFile.write(r);
-
-		QMutexLocker lock(&mutex);
-		already += r.size();
-	}
-	if (got >= total) {
-		reply->deleteLater();
-		reply = 0;
-		outputFile.close();
-		unpackUpdate();
-	} else {
-		emit App::app()->updateDownloading(already, full);
-	}
-}
-
-void PsUpdateDownloader::partFailed(QNetworkReply::NetworkError e) {
-	if (!reply) return;
-
-	QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
-	reply->deleteLater();
-	reply = 0;
-    if (statusCode.isValid()) {
-	    int status = statusCode.toInt();
-		if (status == 416) { // Requested range not satisfiable
-			outputFile.close();
-			unpackUpdate();
-			return;
-		}
-	}
-	LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
-	emit App::app()->updateFailed();
-}
-
-void PsUpdateDownloader::deleteDir(const QString &dir) {
+void psDeleteDir(const QString &dir) {
 	std::wstring wDir = QDir::toNativeSeparators(dir).toStdWString();
 	WCHAR path[4096];
 	memcpy(path, wDir.c_str(), (wDir.size() + 1) * sizeof(WCHAR));
@@ -1582,185 +1405,6 @@ void PsUpdateDownloader::deleteDir(const QString &dir) {
 	int res = SHFileOperation(&file_op);
 }
 
-void PsUpdateDownloader::fatalFail() {
-	clearAll();
-	emit App::app()->updateFailed();
-}
-
-void PsUpdateDownloader::clearAll() {
-	deleteDir(cWorkingDir() + qsl("tupdates"));
-}
-
-QString winapiErrorWrap() {
-	WCHAR errMsg[2048];
-	DWORD errorCode = GetLastError();
-	LPTSTR errorText = NULL, errorTextDefault = L"(Unknown error)";
-	FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, 0, 0);
-	if (!errorText) {
-		errorText = errorTextDefault;
-	}
-	StringCbPrintf(errMsg, sizeof(errMsg), L"Error code: %d, error message: %s", errorCode, errorText);
-	if (errorText != errorTextDefault) {
-		LocalFree(errorText);
-	}
-	return QString::fromWCharArray(errMsg);
-}
-
-void PsUpdateDownloader::unpackUpdate() {
-	QByteArray packed;
-	if (!outputFile.open(QIODevice::ReadOnly)) {
-		LOG(("Update Error: cant read updates file!"));
-		return fatalFail();
-	}
-
-	const int32 hSigLen = 128, hShaLen = 20, hPropsLen = LZMA_PROPS_SIZE, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hPropsLen + hOriginalSizeLen; // header
-
-	QByteArray compressed = outputFile.readAll();
-	int32 compressedLen = compressed.size() - hSize;
-	if (compressedLen <= 0) {
-		LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
-		return fatalFail();
-	}
-	outputFile.close();
-
-	QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyDirPath = cWorkingDir() + qsl("tupdates/ready");
-	deleteDir(tempDirPath);
-	deleteDir(readyDirPath);
-
-	{
-		QDir tempDir(tempDirPath), readyDir(readyDirPath);
-		if (tempDir.exists() || readyDir.exists()) {
-			LOG(("Update Error: cant clear tupdates/temp or tupdates/ready dir!"));
-			return fatalFail();
-		}
-
-		tempDirPath = tempDir.absolutePath();
-		readyDirPath = readyDir.absolutePath();
-
-		uchar sha1Buffer[20];
-		bool goodSha1 = !memcmp(compressed.constData() + hSigLen, hashSha1(compressed.constData() + hSigLen + hShaLen, compressedLen + hPropsLen + hOriginalSizeLen, sha1Buffer), hShaLen);
-		if (!goodSha1) {
-			LOG(("Update Error: bad SHA1 hash of update file!"));
-			return fatalFail();
-		}
-
-		RSA *pbKey = PEM_read_bio_RSAPublicKey(BIO_new_mem_buf(const_cast<char*>(DevChannel ? UpdatesPublicDevKey : UpdatesPublicKey), -1), 0, 0, 0);
-		if (!pbKey) {
-			LOG(("Update Error: cant read public rsa key!"));
-			return fatalFail();
-		}
-		if (RSA_verify(NID_sha1, (const uchar*)(compressed.constData() + hSigLen), hShaLen, (const uchar*)(compressed.constData()), hSigLen, pbKey) != 1) { // verify signature
-			RSA_free(pbKey);
-			LOG(("Update Error: bad RSA signature of update file!"));
-			return fatalFail();
-		}
-		RSA_free(pbKey);
-
-		QByteArray uncompressed;
-
-		int32 uncompressedLen;
-		memcpy(&uncompressedLen, compressed.constData() + hSigLen + hShaLen + hPropsLen, hOriginalSizeLen);
-		uncompressed.resize(uncompressedLen);
-
-		size_t resultLen = uncompressed.size();
-		SizeT srcLen = compressedLen;
-		int uncompressRes = LzmaUncompress((uchar*)uncompressed.data(), &resultLen, (const uchar*)(compressed.constData() + hSize), &srcLen, (const uchar*)(compressed.constData() + hSigLen + hShaLen), LZMA_PROPS_SIZE);
-		if (uncompressRes != SZ_OK) {
-			LOG(("Update Error: could not uncompress lzma, code: %1").arg(uncompressRes));
-			return fatalFail();
-		}
-
-		QDir().mkdir(tempDirPath);
-
-		quint32 version;
-
-		QBuffer buffer(&uncompressed);
-		buffer.open(QIODevice::ReadOnly);
-		QDataStream stream(&buffer);
-		stream.setVersion(QDataStream::Qt_5_1);
-
-		stream >> version;
-		if (stream.status() != QDataStream::Ok) {
-			LOG(("Update Error: cant read version from downloaded stream, status: %1").arg(stream.status()));
-			return fatalFail();
-		}
-		if (version <= AppVersion) {
-			LOG(("Update Error: downloaded version %1 is not greater, than mine %2").arg(version).arg(AppVersion));
-			return fatalFail();
-		}
-
-		quint32 filesCount;
-		stream >> filesCount;
-		if (stream.status() != QDataStream::Ok) {
-			LOG(("Update Error: cant read files count from downloaded stream, status: %1").arg(stream.status()));
-			return fatalFail();
-		}
-		if (!filesCount) {
-			LOG(("Update Error: update is empty!"));
-			return fatalFail();
-		}
-		for (int32 i = 0; i < filesCount; ++i) {
-			QString relativeName;
-			quint32 fileSize;
-			QByteArray fileInnerData;
-
-			stream >> relativeName >> fileSize >> fileInnerData;
-			if (stream.status() != QDataStream::Ok) {
-				LOG(("Update Error: cant read file from downloaded stream, status: %1").arg(stream.status()));
-				return fatalFail();
-			}
-			if (fileSize != fileInnerData.size()) {
-				LOG(("Update Error: bad file size %1 not matching data size %2").arg(fileSize).arg(fileInnerData.size()));
-				return fatalFail();
-			}
-
-			QFile f(tempDirPath + '/' + relativeName);
-			if (!f.open(QIODevice::WriteOnly)) {
-				LOG(("Update Error: cant open file '%1' for writing").arg(tempDirPath + '/' + relativeName));
-				return fatalFail();
-			}
-			if (f.write(fileInnerData) != fileSize) {
-				f.close();
-				LOG(("Update Error: cant write file '%1'").arg(tempDirPath + '/' + relativeName));
-				return fatalFail();
-			}
-			f.close();
-		}
-
-		// create tdata/version file
-		QDir().mkdir(tempDirPath + qsl("/tdata"));
-		std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString();
-		DWORD versionNum = DWORD(version), versionLen = DWORD(versionString.size() * sizeof(WCHAR));
-		WCHAR versionStr[32];
-		memcpy(versionStr, versionString.c_str(), versionLen);
-
-		QFile fVersion(tempDirPath + qsl("/tdata/version"));
-		if (!fVersion.open(QIODevice::WriteOnly)) {
-			LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/tdata/version")));
-			return fatalFail();
-		}
-		fVersion.write((const char*)&versionNum, sizeof(DWORD));
-		fVersion.write((const char*)&versionLen, sizeof(DWORD));
-		fVersion.write((const char*)&versionStr[0], versionLen);
-		fVersion.close();
-	}
-
-	std::wstring tempDirNative = QDir::toNativeSeparators(tempDirPath).toStdWString(), readyDirNative = QDir::toNativeSeparators(readyDirPath).toStdWString();
-	if (!MoveFile(tempDirNative.c_str(), readyDirNative.c_str())) {
-		LOG(("Update Error: cant rename temp dir '%1' to ready dir '%2'. %3").arg(QString::fromStdWString(tempDirNative)).arg(QString::fromStdWString(readyDirNative)).arg(winapiErrorWrap()));
-		return fatalFail();
-	}
-	deleteDir(tempDirPath);
-	outputFile.remove();
-
-	emit App::app()->updateReady();
-}
-
-PsUpdateDownloader::~PsUpdateDownloader() {
-	delete reply;
-	reply = 0;
-}
-
 namespace {
 	BOOL CALLBACK _ActivateProcess(HWND hWnd, LPARAM lParam) {
 		uint64 &processId(*(uint64*)lParam);
@@ -2134,65 +1778,6 @@ int psFixPrevious() {
 	return 0;
 }
 
-bool psCheckReadyUpdate() {
-	QString readyPath = cWorkingDir() + qsl("tupdates/ready");
-	if (!QDir(readyPath).exists()) {
-		return false;
-	}
-
-	// check ready version
-	QString versionPath = readyPath + qsl("/tdata/version");
-	{
-		QFile fVersion(versionPath);
-		if (!fVersion.open(QIODevice::ReadOnly)) {
-			LOG(("Update Error: cant read version file '%1'").arg(versionPath));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		DWORD versionNum;
-		if (fVersion.read((char*)&versionNum, sizeof(DWORD)) != sizeof(DWORD)) {
-			LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		fVersion.close();
-		if (versionNum <= AppVersion) {
-			LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-	}
-
-	QString curUpdater = (cExeDir() + qsl("Updater.exe"));
-	QFileInfo updater(cWorkingDir() + qsl("tupdates/ready/Updater.exe"));
-	if (!updater.exists()) {
-		QFileInfo current(curUpdater);
-		if (!current.exists()) {
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-		if (CopyFile(current.absoluteFilePath().toStdWString().c_str(), updater.absoluteFilePath().toStdWString().c_str(), TRUE) == FALSE) {
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-	}
-	if (CopyFile(updater.absoluteFilePath().toStdWString().c_str(), curUpdater.toStdWString().c_str(), FALSE) == FALSE) {
-		DWORD errorCode = GetLastError();
-		if (errorCode == ERROR_ACCESS_DENIED) { // we are in write-protected dir, like Program Files
-			cSetWriteProtected(true);
-			return true;
-		} else {
-			PsUpdateDownloader::clearAll();
-			return false;
-		}
-	}
-	if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
-		PsUpdateDownloader::clearAll();
-		return false;
-	}
-	return true;
-}
-
 void psPostprocessFile(const QString &name) {
 	std::wstring zoneFile = QDir::toNativeSeparators(name).toStdWString() + L":Zone.Identifier";
 	HANDLE f = CreateFile(zoneFile.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
@@ -2474,7 +2059,7 @@ void psExecUpdater() {
 	if (cStartInTray()) targs += qsl(" -startintray");
 	if (cWriteProtected()) targs += qsl(" -writeprotected \"") + cExeDir() + '"';
 
-	QString updaterPath = cWriteProtected() ? (cWorkingDir() + qsl("tupdates/ready/Updater.exe")) : (cExeDir() + qsl("Updater.exe"));
+	QString updaterPath = cWriteProtected() ? (cWorkingDir() + qsl("tupdates/temp/Updater.exe")) : (cExeDir() + qsl("Updater.exe"));
 
 	QString updater(QDir::toNativeSeparators(updaterPath)), wdir(QDir::toNativeSeparators(cWorkingDir()));
 
@@ -2482,8 +2067,7 @@ void psExecUpdater() {
 	HINSTANCE r = ShellExecute(0, cWriteProtected() ? L"runas" : 0, updater.toStdWString().c_str(), targs.toStdWString().c_str(), wdir.isEmpty() ? 0 : wdir.toStdWString().c_str(), SW_SHOWNORMAL);
 	if (long(r) < 32) {
 		DEBUG_LOG(("Application Error: failed to execute %1, working directory: '%2', result: %3").arg(updater).arg(wdir).arg(long(r)));
-		QString readyPath = cWorkingDir() + qsl("tupdates/ready");
-		PsUpdateDownloader::deleteDir(readyPath);
+		psDeleteDir(cWorkingDir() + qsl("tupdates/temp"));
 	}
 }
 
diff --git a/Telegram/SourceFiles/pspecific_wnd.h b/Telegram/SourceFiles/pspecific_wnd.h
index 99ed0b8504..6e8040b239 100644
--- a/Telegram/SourceFiles/pspecific_wnd.h
+++ b/Telegram/SourceFiles/pspecific_wnd.h
@@ -117,55 +117,9 @@ public:
 	void psInstallEventFilter();
 	~PsApplication();
 
-signals:
-
-	void updateChecking();
-	void updateLatest();
-	void updateDownloading(qint64 ready, qint64 total);
-	void updateReady();
-	void updateFailed();
-
 };
 
-class PsUpdateDownloader : public QObject {
-	Q_OBJECT
-
-public:
-	PsUpdateDownloader(QThread *thread, const MTPDhelp_appUpdate &update);
-	PsUpdateDownloader(QThread *thread, const QString &url);
-
-	void unpackUpdate();
-
-	int32 ready();
-	int32 size();
-
-	static void deleteDir(const QString &dir);
-	static void clearAll();
-
-	~PsUpdateDownloader();
-
-public slots:
-
-	void start();
-	void partMetaGot();
-	void partFinished(qint64 got, qint64 total);
-	void partFailed(QNetworkReply::NetworkError e);
-	void sendRequest();
-
-private:
-	void initOutput();
-
-	void fatalFail();
-
-	QString updateUrl;
-	QNetworkAccessManager manager;
-	QNetworkReply *reply;
-	int32 already, full;
-	QFile outputFile;
-
-	QMutex mutex;
-
-};
+void psDeleteDir(const QString &dir);
 
 void psUserActionDone();
 bool psIdleSupported();
@@ -196,7 +150,6 @@ void psBringToBack(QWidget *w);
 int psCleanup();
 int psFixPrevious();
 
-bool psCheckReadyUpdate();
 void psExecUpdater();
 void psExecTelegram();
 
diff --git a/Telegram/SourceFiles/settingswidget.cpp b/Telegram/SourceFiles/settingswidget.cpp
index e598810020..17e3c62bb4 100644
--- a/Telegram/SourceFiles/settingswidget.cpp
+++ b/Telegram/SourceFiles/settingswidget.cpp
@@ -37,6 +37,8 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 #include "langloaderplain.h"
 #include "gui/filedialog.h"
 
+#include "autoupdater.h"
+
 #include "localstorage.h"
 
 Slider::Slider(QWidget *parent, const style::slider &st, int32 count, int32 sel) : QWidget(parent),
@@ -1172,7 +1174,7 @@ void SettingsInner::onCheckNow() {
 }
 
 void SettingsInner::onRestartNow() {
-	psCheckReadyUpdate();
+	checkReadyUpdate();
 	if (_updatingState == UpdatingReady) {
 		cSetRestartingUpdate(true);
 	} else {
diff --git a/Telegram/SourceFiles/sysbuttons.cpp b/Telegram/SourceFiles/sysbuttons.cpp
index 6095ba43d0..29e11ac697 100644
--- a/Telegram/SourceFiles/sysbuttons.cpp
+++ b/Telegram/SourceFiles/sysbuttons.cpp
@@ -23,6 +23,7 @@ Copyright (c) 2014 John Preston, https://desktop.telegram.org
 #include "passcodewidget.h"
 #include "window.h"
 #include "application.h"
+#include "autoupdater.h"
 
 SysBtn::SysBtn(QWidget *parent, const style::sysButton &st, const QString &text) : Button(parent),
 _st(st), a_color(_st.color->c), _overLevel(0), _text(text) {
@@ -142,7 +143,7 @@ UpdateBtn::UpdateBtn(QWidget *parent, Window *window, const QString &text) : Sys
 }
 
 void UpdateBtn::onClick() {
-	psCheckReadyUpdate();
+	checkReadyUpdate();
 	if (App::app()->updatingState() == Application::UpdatingReady) {
 		cSetRestartingUpdate(true);
 	} else {
diff --git a/Telegram/Telegram.vcxproj b/Telegram/Telegram.vcxproj
index fa46bff5b1..f392e92807 100644
--- a/Telegram/Telegram.vcxproj
+++ b/Telegram/Telegram.vcxproj
@@ -180,6 +180,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_autoupdater.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Debug\moc_backgroundbox.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -442,6 +446,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Deploy\moc_autoupdater.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Deploy\moc_backgroundbox.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
@@ -729,6 +737,10 @@
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
     </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_autoupdater.cpp">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+    </ClCompile>
     <ClCompile Include="GeneratedFiles\Release\moc_backgroundbox.cpp">
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">true</ExcludedFromBuild>
       <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
@@ -964,6 +976,7 @@
     <ClCompile Include="SourceFiles\app.cpp" />
     <ClCompile Include="SourceFiles\application.cpp" />
     <ClCompile Include="SourceFiles\audio.cpp" />
+    <ClCompile Include="SourceFiles\autoupdater.cpp" />
     <ClCompile Include="SourceFiles\boxes\aboutbox.cpp" />
     <ClCompile Include="SourceFiles\boxes\abstractbox.cpp" />
     <ClCompile Include="SourceFiles\boxes\addcontactbox.cpp" />
@@ -1383,6 +1396,20 @@
       <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
       <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp"  -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG  "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\mpg123-1.22.1\ports\MSVC++" "-I.\..\..\Libraries\mpg123-1.22.1\src\libmpg123" "-I.\..\..\Libraries\faad2-2.7\include" "-I.\..\..\Libraries\faad2-2.7\common\mp4ff" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui" "-fstdafx.h" "-f../../SourceFiles/boxes/stickersetbox.h"</Command>
     </CustomBuild>
+    <CustomBuild Include="SourceFiles\autoupdater.h">
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">Moc%27ing autoupdater.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Deploy|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/autoupdater.h"  -DAL_LIBTYPE_STATIC -DCUSTOM_API_ID -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing autoupdater.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/autoupdater.h"  -DAL_LIBTYPE_STATIC -DUNICODE -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing autoupdater.h...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" "-fstdafx.h" "-f../../SourceFiles/autoupdater.h"  -DAL_LIBTYPE_STATIC -DUNICODE -D_WITH_DEBUG -DWIN32 -DWIN64 -DHAVE_STDINT_H -DZLIB_WINAPI -DQT_NO_DEBUG -DNDEBUG "-I.\..\..\Libraries\lzma\C" "-I.\..\..\Libraries\libexif-0.6.20" "-I.\..\..\Libraries\zlib-1.2.8" "-I.\..\..\Libraries\OpenSSL-Win32\include" "-I.\..\..\Libraries\libogg-1.3.2\include" "-I.\..\..\Libraries\opus\include" "-I.\..\..\Libraries\opusfile\include" "-I.\..\..\Libraries\mpg123-1.22.1\ports\MSVC++" "-I.\..\..\Libraries\mpg123-1.22.1\src\libmpg123" "-I.\..\..\Libraries\faad2-2.7\include" "-I.\..\..\Libraries\faad2-2.7\common\mp4ff" "-I.\..\..\Libraries\ffmpeg-2.6.3" "-I.\..\..\Libraries\openal-soft\include" "-I.\SourceFiles" "-I.\GeneratedFiles" "-I." "-I$(QTDIR)\include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\..\Libraries\QtStatic\qtbase\include\QtCore\5.4.0\QtCore" "-I.\..\..\Libraries\QtStatic\qtbase\include\QtGui\5.4.0\QtGui"</Command>
+    </CustomBuild>
     <ClInclude Include="SourceFiles\config.h" />
     <CustomBuild Include="SourceFiles\gui\animation.h">
       <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing animation.h...</Message>
diff --git a/Telegram/Telegram.vcxproj.filters b/Telegram/Telegram.vcxproj.filters
index 74e10dc9c1..6694d680f8 100644
--- a/Telegram/Telegram.vcxproj.filters
+++ b/Telegram/Telegram.vcxproj.filters
@@ -891,6 +891,18 @@
     <ClCompile Include="SourceFiles\boxes\stickersetbox.cpp">
       <Filter>boxes</Filter>
     </ClCompile>
+    <ClCompile Include="SourceFiles\autoupdater.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Deploy\moc_autoupdater.cpp">
+      <Filter>Generated Files\Deploy</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Debug\moc_autoupdater.cpp">
+      <Filter>Generated Files\Debug</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\Release\moc_autoupdater.cpp">
+      <Filter>Generated Files\Release</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="SourceFiles\stdafx.h">
@@ -1183,6 +1195,9 @@
     <CustomBuild Include="SourceFiles\boxes\stickersetbox.h">
       <Filter>boxes</Filter>
     </CustomBuild>
+    <CustomBuild Include="SourceFiles\autoupdater.h">
+      <Filter>Source Files</Filter>
+    </CustomBuild>
   </ItemGroup>
   <ItemGroup>
     <Image Include="SourceFiles\art\icon256.ico" />
diff --git a/Telegram/_openal_patch.diff b/Telegram/_openal_patch.diff
new file mode 100644
index 0000000000..951e0e1c0d
--- /dev/null
+++ b/Telegram/_openal_patch.diff
@@ -0,0 +1,35 @@
+diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c
+index cfd12d8..8a6f9fb 100644
+--- a/Alc/backends/mmdevapi.c
++++ b/Alc/backends/mmdevapi.c
+@@ -1719,7 +1719,7 @@ static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
+ 
+ static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
+ {
+-    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
++    if(type == ALCbackend_Playback/* || type == ALCbackend_Capture*/)
+         return ALC_TRUE;
+     return ALC_FALSE;
+ }
+diff --git a/Alc/backends/winmm.c b/Alc/backends/winmm.c
+index 03805ab..77212c2 100644
+--- a/Alc/backends/winmm.c
++++ b/Alc/backends/winmm.c
+@@ -220,7 +220,7 @@ FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
+     SetRTPriority();
+     althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+ 
+-    while(GetMessage(&msg, NULL, 0, 0))
++	if (!self->killNow) while (GetMessage(&msg, NULL, 0, 0))
+     {
+         if(msg.message != WOM_DONE)
+             continue;
+@@ -505,7 +505,7 @@ static int ALCwinmmCapture_captureProc(void *arg)
+ 
+     althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+ 
+-    while(GetMessage(&msg, NULL, 0, 0))
++    if (!self->killNow) while(GetMessage(&msg, NULL, 0, 0))
+     {
+         if(msg.message != WIM_DATA)
+             continue;
diff --git a/Telegram/_openal_patch/Alc/ALc.c b/Telegram/_openal_patch/Alc/ALc.c
deleted file mode 100644
index 34104fb26a..0000000000
--- a/Telegram/_openal_patch/Alc/ALc.c
+++ /dev/null
@@ -1,3771 +0,0 @@
-/**
- * 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 <math.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <memory.h>
-#include <ctype.h>
-#include <signal.h>
-
-#include "alMain.h"
-#include "alSource.h"
-#include "alListener.h"
-#include "alThunk.h"
-#include "alSource.h"
-#include "alBuffer.h"
-#include "alAuxEffectSlot.h"
-#include "alError.h"
-#include "alMidi.h"
-#include "bs2b.h"
-#include "alu.h"
-
-#include "compat.h"
-#include "threads.h"
-#include "alstring.h"
-
-#include "backends/base.h"
-#include "midi/base.h"
-
-
-/************************************************
- * Backends
- ************************************************/
-struct BackendInfo {
-    const char *name;
-    ALCbackendFactory* (*getFactory)(void);
-    ALCboolean (*Init)(BackendFuncs*);
-    void (*Deinit)(void);
-    void (*Probe)(enum DevProbe);
-    BackendFuncs Funcs;
-};
-
-#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
-static struct BackendInfo BackendList[] = {
-#ifdef HAVE_PULSEAUDIO
-    { "pulse", ALCpulseBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
-#endif
-#ifdef HAVE_ALSA
-    { "alsa", ALCalsaBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
-#endif
-#ifdef HAVE_COREAUDIO
-    { "core", NULL, alc_ca_init, alc_ca_deinit, alc_ca_probe, EmptyFuncs },
-#endif
-#ifdef HAVE_OSS
-    { "oss", ALCossBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
-#endif
-#ifdef HAVE_SOLARIS
-    { "solaris", NULL, alc_solaris_init, alc_solaris_deinit, alc_solaris_probe, EmptyFuncs },
-#endif
-#ifdef HAVE_SNDIO
-    { "sndio", NULL, alc_sndio_init, alc_sndio_deinit, alc_sndio_probe, EmptyFuncs },
-#endif
-#ifdef HAVE_QSA
-    { "qsa", NULL, alc_qsa_init, alc_qsa_deinit, alc_qsa_probe, EmptyFuncs },
-#endif
-#ifdef HAVE_MMDEVAPI
-    { "mmdevapi", ALCmmdevBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
-#endif
-#ifdef HAVE_DSOUND
-    { "dsound", ALCdsoundBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
-#endif
-#ifdef HAVE_WINMM
-    { "winmm", NULL, alcWinMMInit, alcWinMMDeinit, alcWinMMProbe, EmptyFuncs },
-#endif
-#ifdef HAVE_PORTAUDIO
-    { "port", NULL, alc_pa_init, alc_pa_deinit, alc_pa_probe, EmptyFuncs },
-#endif
-#ifdef HAVE_OPENSL
-    { "opensl", NULL, alc_opensl_init, alc_opensl_deinit, alc_opensl_probe, EmptyFuncs },
-#endif
-
-    { "null", ALCnullBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
-#ifdef HAVE_WAVE
-    { "wave", ALCwaveBackendFactory_getFactory, NULL, NULL, NULL, EmptyFuncs },
-#endif
-
-    { NULL, NULL, NULL, NULL, NULL, EmptyFuncs }
-};
-#undef EmptyFuncs
-
-static struct BackendInfo PlaybackBackend;
-static struct BackendInfo CaptureBackend;
-
-static bool SuspendAndProcessSupported = false;
-
-
-/************************************************
- * Functions, enums, and errors
- ************************************************/
-typedef struct ALCfunction {
-    const ALCchar *funcName;
-    ALCvoid *address;
-} ALCfunction;
-
-typedef struct ALCenums {
-    const ALCchar *enumName;
-    ALCenum value;
-} ALCenums;
-
-#define DECL(x) { #x, (ALCvoid*)(x) }
-static const ALCfunction alcFunctions[] = {
-    DECL(alcCreateContext),
-    DECL(alcMakeContextCurrent),
-    DECL(alcProcessContext),
-    DECL(alcSuspendContext),
-    DECL(alcDestroyContext),
-    DECL(alcGetCurrentContext),
-    DECL(alcGetContextsDevice),
-    DECL(alcOpenDevice),
-    DECL(alcCloseDevice),
-    DECL(alcGetError),
-    DECL(alcIsExtensionPresent),
-    DECL(alcGetProcAddress),
-    DECL(alcGetEnumValue),
-    DECL(alcGetString),
-    DECL(alcGetIntegerv),
-    DECL(alcCaptureOpenDevice),
-    DECL(alcCaptureCloseDevice),
-    DECL(alcCaptureStart),
-    DECL(alcCaptureStop),
-    DECL(alcCaptureSamples),
-
-    DECL(alcSetThreadContext),
-    DECL(alcGetThreadContext),
-
-    DECL(alcLoopbackOpenDeviceSOFT),
-    DECL(alcIsRenderFormatSupportedSOFT),
-    DECL(alcRenderSamplesSOFT),
-
-    DECL(alcDevicePauseSOFT),
-    DECL(alcDeviceResumeSOFT),
-
-    DECL(alcGetInteger64vSOFT),
-
-    DECL(alEnable),
-    DECL(alDisable),
-    DECL(alIsEnabled),
-    DECL(alGetString),
-    DECL(alGetBooleanv),
-    DECL(alGetIntegerv),
-    DECL(alGetFloatv),
-    DECL(alGetDoublev),
-    DECL(alGetBoolean),
-    DECL(alGetInteger),
-    DECL(alGetFloat),
-    DECL(alGetDouble),
-    DECL(alGetError),
-    DECL(alIsExtensionPresent),
-    DECL(alGetProcAddress),
-    DECL(alGetEnumValue),
-    DECL(alListenerf),
-    DECL(alListener3f),
-    DECL(alListenerfv),
-    DECL(alListeneri),
-    DECL(alListener3i),
-    DECL(alListeneriv),
-    DECL(alGetListenerf),
-    DECL(alGetListener3f),
-    DECL(alGetListenerfv),
-    DECL(alGetListeneri),
-    DECL(alGetListener3i),
-    DECL(alGetListeneriv),
-    DECL(alGenSources),
-    DECL(alDeleteSources),
-    DECL(alIsSource),
-    DECL(alSourcef),
-    DECL(alSource3f),
-    DECL(alSourcefv),
-    DECL(alSourcei),
-    DECL(alSource3i),
-    DECL(alSourceiv),
-    DECL(alGetSourcef),
-    DECL(alGetSource3f),
-    DECL(alGetSourcefv),
-    DECL(alGetSourcei),
-    DECL(alGetSource3i),
-    DECL(alGetSourceiv),
-    DECL(alSourcePlayv),
-    DECL(alSourceStopv),
-    DECL(alSourceRewindv),
-    DECL(alSourcePausev),
-    DECL(alSourcePlay),
-    DECL(alSourceStop),
-    DECL(alSourceRewind),
-    DECL(alSourcePause),
-    DECL(alSourceQueueBuffers),
-    DECL(alSourceUnqueueBuffers),
-    DECL(alGenBuffers),
-    DECL(alDeleteBuffers),
-    DECL(alIsBuffer),
-    DECL(alBufferData),
-    DECL(alBufferf),
-    DECL(alBuffer3f),
-    DECL(alBufferfv),
-    DECL(alBufferi),
-    DECL(alBuffer3i),
-    DECL(alBufferiv),
-    DECL(alGetBufferf),
-    DECL(alGetBuffer3f),
-    DECL(alGetBufferfv),
-    DECL(alGetBufferi),
-    DECL(alGetBuffer3i),
-    DECL(alGetBufferiv),
-    DECL(alDopplerFactor),
-    DECL(alDopplerVelocity),
-    DECL(alSpeedOfSound),
-    DECL(alDistanceModel),
-
-    DECL(alGenFilters),
-    DECL(alDeleteFilters),
-    DECL(alIsFilter),
-    DECL(alFilteri),
-    DECL(alFilteriv),
-    DECL(alFilterf),
-    DECL(alFilterfv),
-    DECL(alGetFilteri),
-    DECL(alGetFilteriv),
-    DECL(alGetFilterf),
-    DECL(alGetFilterfv),
-    DECL(alGenEffects),
-    DECL(alDeleteEffects),
-    DECL(alIsEffect),
-    DECL(alEffecti),
-    DECL(alEffectiv),
-    DECL(alEffectf),
-    DECL(alEffectfv),
-    DECL(alGetEffecti),
-    DECL(alGetEffectiv),
-    DECL(alGetEffectf),
-    DECL(alGetEffectfv),
-    DECL(alGenAuxiliaryEffectSlots),
-    DECL(alDeleteAuxiliaryEffectSlots),
-    DECL(alIsAuxiliaryEffectSlot),
-    DECL(alAuxiliaryEffectSloti),
-    DECL(alAuxiliaryEffectSlotiv),
-    DECL(alAuxiliaryEffectSlotf),
-    DECL(alAuxiliaryEffectSlotfv),
-    DECL(alGetAuxiliaryEffectSloti),
-    DECL(alGetAuxiliaryEffectSlotiv),
-    DECL(alGetAuxiliaryEffectSlotf),
-    DECL(alGetAuxiliaryEffectSlotfv),
-
-    DECL(alBufferSubDataSOFT),
-
-    DECL(alBufferSamplesSOFT),
-    DECL(alBufferSubSamplesSOFT),
-    DECL(alGetBufferSamplesSOFT),
-    DECL(alIsBufferFormatSupportedSOFT),
-
-    DECL(alDeferUpdatesSOFT),
-    DECL(alProcessUpdatesSOFT),
-
-    DECL(alSourcedSOFT),
-    DECL(alSource3dSOFT),
-    DECL(alSourcedvSOFT),
-    DECL(alGetSourcedSOFT),
-    DECL(alGetSource3dSOFT),
-    DECL(alGetSourcedvSOFT),
-    DECL(alSourcei64SOFT),
-    DECL(alSource3i64SOFT),
-    DECL(alSourcei64vSOFT),
-    DECL(alGetSourcei64SOFT),
-    DECL(alGetSource3i64SOFT),
-    DECL(alGetSourcei64vSOFT),
-
-    DECL(alGenSoundfontsSOFT),
-    DECL(alDeleteSoundfontsSOFT),
-    DECL(alIsSoundfontSOFT),
-    DECL(alGetSoundfontivSOFT),
-    DECL(alSoundfontPresetsSOFT),
-    DECL(alGenPresetsSOFT),
-    DECL(alDeletePresetsSOFT),
-    DECL(alIsPresetSOFT),
-    DECL(alPresetiSOFT),
-    DECL(alPresetivSOFT),
-    DECL(alGetPresetivSOFT),
-    DECL(alPresetFontsoundsSOFT),
-    DECL(alGenFontsoundsSOFT),
-    DECL(alDeleteFontsoundsSOFT),
-    DECL(alIsFontsoundSOFT),
-    DECL(alFontsoundiSOFT),
-    DECL(alFontsound2iSOFT),
-    DECL(alFontsoundivSOFT),
-    DECL(alGetFontsoundivSOFT),
-    DECL(alFontsoundModulatoriSOFT),
-    DECL(alGetFontsoundModulatorivSOFT),
-    DECL(alMidiSoundfontSOFT),
-    DECL(alMidiSoundfontvSOFT),
-    DECL(alMidiEventSOFT),
-    DECL(alMidiSysExSOFT),
-    DECL(alMidiPlaySOFT),
-    DECL(alMidiPauseSOFT),
-    DECL(alMidiStopSOFT),
-    DECL(alMidiResetSOFT),
-    DECL(alMidiGainSOFT),
-    DECL(alGetInteger64SOFT),
-    DECL(alGetInteger64vSOFT),
-    DECL(alLoadSoundfontSOFT),
-
-    { NULL, NULL }
-};
-#undef DECL
-
-#define DECL(x) { #x, (x) }
-static const ALCenums enumeration[] = {
-    DECL(ALC_INVALID),
-    DECL(ALC_FALSE),
-    DECL(ALC_TRUE),
-
-    DECL(ALC_MAJOR_VERSION),
-    DECL(ALC_MINOR_VERSION),
-    DECL(ALC_ATTRIBUTES_SIZE),
-    DECL(ALC_ALL_ATTRIBUTES),
-    DECL(ALC_DEFAULT_DEVICE_SPECIFIER),
-    DECL(ALC_DEVICE_SPECIFIER),
-    DECL(ALC_ALL_DEVICES_SPECIFIER),
-    DECL(ALC_DEFAULT_ALL_DEVICES_SPECIFIER),
-    DECL(ALC_EXTENSIONS),
-    DECL(ALC_FREQUENCY),
-    DECL(ALC_REFRESH),
-    DECL(ALC_SYNC),
-    DECL(ALC_MONO_SOURCES),
-    DECL(ALC_STEREO_SOURCES),
-    DECL(ALC_CAPTURE_DEVICE_SPECIFIER),
-    DECL(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER),
-    DECL(ALC_CAPTURE_SAMPLES),
-    DECL(ALC_CONNECTED),
-
-    DECL(ALC_EFX_MAJOR_VERSION),
-    DECL(ALC_EFX_MINOR_VERSION),
-    DECL(ALC_MAX_AUXILIARY_SENDS),
-
-    DECL(ALC_FORMAT_CHANNELS_SOFT),
-    DECL(ALC_FORMAT_TYPE_SOFT),
-
-    DECL(ALC_MONO_SOFT),
-    DECL(ALC_STEREO_SOFT),
-    DECL(ALC_QUAD_SOFT),
-    DECL(ALC_5POINT1_SOFT),
-    DECL(ALC_6POINT1_SOFT),
-    DECL(ALC_7POINT1_SOFT),
-
-    DECL(ALC_BYTE_SOFT),
-    DECL(ALC_UNSIGNED_BYTE_SOFT),
-    DECL(ALC_SHORT_SOFT),
-    DECL(ALC_UNSIGNED_SHORT_SOFT),
-    DECL(ALC_INT_SOFT),
-    DECL(ALC_UNSIGNED_INT_SOFT),
-    DECL(ALC_FLOAT_SOFT),
-
-    DECL(ALC_NO_ERROR),
-    DECL(ALC_INVALID_DEVICE),
-    DECL(ALC_INVALID_CONTEXT),
-    DECL(ALC_INVALID_ENUM),
-    DECL(ALC_INVALID_VALUE),
-    DECL(ALC_OUT_OF_MEMORY),
-
-
-    DECL(AL_INVALID),
-    DECL(AL_NONE),
-    DECL(AL_FALSE),
-    DECL(AL_TRUE),
-
-    DECL(AL_SOURCE_RELATIVE),
-    DECL(AL_CONE_INNER_ANGLE),
-    DECL(AL_CONE_OUTER_ANGLE),
-    DECL(AL_PITCH),
-    DECL(AL_POSITION),
-    DECL(AL_DIRECTION),
-    DECL(AL_VELOCITY),
-    DECL(AL_LOOPING),
-    DECL(AL_BUFFER),
-    DECL(AL_GAIN),
-    DECL(AL_MIN_GAIN),
-    DECL(AL_MAX_GAIN),
-    DECL(AL_ORIENTATION),
-    DECL(AL_REFERENCE_DISTANCE),
-    DECL(AL_ROLLOFF_FACTOR),
-    DECL(AL_CONE_OUTER_GAIN),
-    DECL(AL_MAX_DISTANCE),
-    DECL(AL_SEC_OFFSET),
-    DECL(AL_SAMPLE_OFFSET),
-    DECL(AL_SAMPLE_RW_OFFSETS_SOFT),
-    DECL(AL_BYTE_OFFSET),
-    DECL(AL_BYTE_RW_OFFSETS_SOFT),
-    DECL(AL_SOURCE_TYPE),
-    DECL(AL_STATIC),
-    DECL(AL_STREAMING),
-    DECL(AL_UNDETERMINED),
-    DECL(AL_METERS_PER_UNIT),
-    DECL(AL_DIRECT_CHANNELS_SOFT),
-
-    DECL(AL_DIRECT_FILTER),
-    DECL(AL_AUXILIARY_SEND_FILTER),
-    DECL(AL_AIR_ABSORPTION_FACTOR),
-    DECL(AL_ROOM_ROLLOFF_FACTOR),
-    DECL(AL_CONE_OUTER_GAINHF),
-    DECL(AL_DIRECT_FILTER_GAINHF_AUTO),
-    DECL(AL_AUXILIARY_SEND_FILTER_GAIN_AUTO),
-    DECL(AL_AUXILIARY_SEND_FILTER_GAINHF_AUTO),
-
-    DECL(AL_SOURCE_STATE),
-    DECL(AL_INITIAL),
-    DECL(AL_PLAYING),
-    DECL(AL_PAUSED),
-    DECL(AL_STOPPED),
-
-    DECL(AL_BUFFERS_QUEUED),
-    DECL(AL_BUFFERS_PROCESSED),
-
-    DECL(AL_FORMAT_MONO8),
-    DECL(AL_FORMAT_MONO16),
-    DECL(AL_FORMAT_MONO_FLOAT32),
-    DECL(AL_FORMAT_MONO_DOUBLE_EXT),
-    DECL(AL_FORMAT_STEREO8),
-    DECL(AL_FORMAT_STEREO16),
-    DECL(AL_FORMAT_STEREO_FLOAT32),
-    DECL(AL_FORMAT_STEREO_DOUBLE_EXT),
-    DECL(AL_FORMAT_MONO_IMA4),
-    DECL(AL_FORMAT_STEREO_IMA4),
-    DECL(AL_FORMAT_MONO_MSADPCM_SOFT),
-    DECL(AL_FORMAT_STEREO_MSADPCM_SOFT),
-    DECL(AL_FORMAT_QUAD8_LOKI),
-    DECL(AL_FORMAT_QUAD16_LOKI),
-    DECL(AL_FORMAT_QUAD8),
-    DECL(AL_FORMAT_QUAD16),
-    DECL(AL_FORMAT_QUAD32),
-    DECL(AL_FORMAT_51CHN8),
-    DECL(AL_FORMAT_51CHN16),
-    DECL(AL_FORMAT_51CHN32),
-    DECL(AL_FORMAT_61CHN8),
-    DECL(AL_FORMAT_61CHN16),
-    DECL(AL_FORMAT_61CHN32),
-    DECL(AL_FORMAT_71CHN8),
-    DECL(AL_FORMAT_71CHN16),
-    DECL(AL_FORMAT_71CHN32),
-    DECL(AL_FORMAT_REAR8),
-    DECL(AL_FORMAT_REAR16),
-    DECL(AL_FORMAT_REAR32),
-    DECL(AL_FORMAT_MONO_MULAW),
-    DECL(AL_FORMAT_MONO_MULAW_EXT),
-    DECL(AL_FORMAT_STEREO_MULAW),
-    DECL(AL_FORMAT_STEREO_MULAW_EXT),
-    DECL(AL_FORMAT_QUAD_MULAW),
-    DECL(AL_FORMAT_51CHN_MULAW),
-    DECL(AL_FORMAT_61CHN_MULAW),
-    DECL(AL_FORMAT_71CHN_MULAW),
-    DECL(AL_FORMAT_REAR_MULAW),
-    DECL(AL_FORMAT_MONO_ALAW_EXT),
-    DECL(AL_FORMAT_STEREO_ALAW_EXT),
-
-    DECL(AL_MONO8_SOFT),
-    DECL(AL_MONO16_SOFT),
-    DECL(AL_MONO32F_SOFT),
-    DECL(AL_STEREO8_SOFT),
-    DECL(AL_STEREO16_SOFT),
-    DECL(AL_STEREO32F_SOFT),
-    DECL(AL_QUAD8_SOFT),
-    DECL(AL_QUAD16_SOFT),
-    DECL(AL_QUAD32F_SOFT),
-    DECL(AL_REAR8_SOFT),
-    DECL(AL_REAR16_SOFT),
-    DECL(AL_REAR32F_SOFT),
-    DECL(AL_5POINT1_8_SOFT),
-    DECL(AL_5POINT1_16_SOFT),
-    DECL(AL_5POINT1_32F_SOFT),
-    DECL(AL_6POINT1_8_SOFT),
-    DECL(AL_6POINT1_16_SOFT),
-    DECL(AL_6POINT1_32F_SOFT),
-    DECL(AL_7POINT1_8_SOFT),
-    DECL(AL_7POINT1_16_SOFT),
-    DECL(AL_7POINT1_32F_SOFT),
-
-    DECL(AL_MONO_SOFT),
-    DECL(AL_STEREO_SOFT),
-    DECL(AL_QUAD_SOFT),
-    DECL(AL_REAR_SOFT),
-    DECL(AL_5POINT1_SOFT),
-    DECL(AL_6POINT1_SOFT),
-    DECL(AL_7POINT1_SOFT),
-
-    DECL(AL_BYTE_SOFT),
-    DECL(AL_UNSIGNED_BYTE_SOFT),
-    DECL(AL_SHORT_SOFT),
-    DECL(AL_UNSIGNED_SHORT_SOFT),
-    DECL(AL_INT_SOFT),
-    DECL(AL_UNSIGNED_INT_SOFT),
-    DECL(AL_FLOAT_SOFT),
-    DECL(AL_DOUBLE_SOFT),
-    DECL(AL_BYTE3_SOFT),
-    DECL(AL_UNSIGNED_BYTE3_SOFT),
-
-    DECL(AL_FREQUENCY),
-    DECL(AL_BITS),
-    DECL(AL_CHANNELS),
-    DECL(AL_SIZE),
-    DECL(AL_INTERNAL_FORMAT_SOFT),
-    DECL(AL_BYTE_LENGTH_SOFT),
-    DECL(AL_SAMPLE_LENGTH_SOFT),
-    DECL(AL_SEC_LENGTH_SOFT),
-    DECL(AL_UNPACK_BLOCK_ALIGNMENT_SOFT),
-    DECL(AL_PACK_BLOCK_ALIGNMENT_SOFT),
-
-    DECL(AL_UNUSED),
-    DECL(AL_PENDING),
-    DECL(AL_PROCESSED),
-
-    DECL(AL_NO_ERROR),
-    DECL(AL_INVALID_NAME),
-    DECL(AL_INVALID_ENUM),
-    DECL(AL_INVALID_VALUE),
-    DECL(AL_INVALID_OPERATION),
-    DECL(AL_OUT_OF_MEMORY),
-
-    DECL(AL_VENDOR),
-    DECL(AL_VERSION),
-    DECL(AL_RENDERER),
-    DECL(AL_EXTENSIONS),
-
-    DECL(AL_DOPPLER_FACTOR),
-    DECL(AL_DOPPLER_VELOCITY),
-    DECL(AL_DISTANCE_MODEL),
-    DECL(AL_SPEED_OF_SOUND),
-    DECL(AL_SOURCE_DISTANCE_MODEL),
-    DECL(AL_DEFERRED_UPDATES_SOFT),
-
-    DECL(AL_INVERSE_DISTANCE),
-    DECL(AL_INVERSE_DISTANCE_CLAMPED),
-    DECL(AL_LINEAR_DISTANCE),
-    DECL(AL_LINEAR_DISTANCE_CLAMPED),
-    DECL(AL_EXPONENT_DISTANCE),
-    DECL(AL_EXPONENT_DISTANCE_CLAMPED),
-
-    DECL(AL_FILTER_TYPE),
-    DECL(AL_FILTER_NULL),
-    DECL(AL_FILTER_LOWPASS),
-    DECL(AL_FILTER_HIGHPASS),
-    DECL(AL_FILTER_BANDPASS),
-
-    DECL(AL_LOWPASS_GAIN),
-    DECL(AL_LOWPASS_GAINHF),
-
-    DECL(AL_HIGHPASS_GAIN),
-    DECL(AL_HIGHPASS_GAINLF),
-
-    DECL(AL_BANDPASS_GAIN),
-    DECL(AL_BANDPASS_GAINHF),
-    DECL(AL_BANDPASS_GAINLF),
-
-    DECL(AL_EFFECT_TYPE),
-    DECL(AL_EFFECT_NULL),
-    DECL(AL_EFFECT_REVERB),
-    DECL(AL_EFFECT_EAXREVERB),
-    DECL(AL_EFFECT_CHORUS),
-    DECL(AL_EFFECT_DISTORTION),
-    DECL(AL_EFFECT_ECHO),
-    DECL(AL_EFFECT_FLANGER),
-#if 0
-    DECL(AL_EFFECT_FREQUENCY_SHIFTER),
-    DECL(AL_EFFECT_VOCAL_MORPHER),
-    DECL(AL_EFFECT_PITCH_SHIFTER),
-#endif
-    DECL(AL_EFFECT_RING_MODULATOR),
-#if 0
-    DECL(AL_EFFECT_AUTOWAH),
-#endif
-    DECL(AL_EFFECT_COMPRESSOR),
-    DECL(AL_EFFECT_EQUALIZER),
-    DECL(AL_EFFECT_DEDICATED_LOW_FREQUENCY_EFFECT),
-    DECL(AL_EFFECT_DEDICATED_DIALOGUE),
-
-    DECL(AL_EAXREVERB_DENSITY),
-    DECL(AL_EAXREVERB_DIFFUSION),
-    DECL(AL_EAXREVERB_GAIN),
-    DECL(AL_EAXREVERB_GAINHF),
-    DECL(AL_EAXREVERB_GAINLF),
-    DECL(AL_EAXREVERB_DECAY_TIME),
-    DECL(AL_EAXREVERB_DECAY_HFRATIO),
-    DECL(AL_EAXREVERB_DECAY_LFRATIO),
-    DECL(AL_EAXREVERB_REFLECTIONS_GAIN),
-    DECL(AL_EAXREVERB_REFLECTIONS_DELAY),
-    DECL(AL_EAXREVERB_REFLECTIONS_PAN),
-    DECL(AL_EAXREVERB_LATE_REVERB_GAIN),
-    DECL(AL_EAXREVERB_LATE_REVERB_DELAY),
-    DECL(AL_EAXREVERB_LATE_REVERB_PAN),
-    DECL(AL_EAXREVERB_ECHO_TIME),
-    DECL(AL_EAXREVERB_ECHO_DEPTH),
-    DECL(AL_EAXREVERB_MODULATION_TIME),
-    DECL(AL_EAXREVERB_MODULATION_DEPTH),
-    DECL(AL_EAXREVERB_AIR_ABSORPTION_GAINHF),
-    DECL(AL_EAXREVERB_HFREFERENCE),
-    DECL(AL_EAXREVERB_LFREFERENCE),
-    DECL(AL_EAXREVERB_ROOM_ROLLOFF_FACTOR),
-    DECL(AL_EAXREVERB_DECAY_HFLIMIT),
-
-    DECL(AL_REVERB_DENSITY),
-    DECL(AL_REVERB_DIFFUSION),
-    DECL(AL_REVERB_GAIN),
-    DECL(AL_REVERB_GAINHF),
-    DECL(AL_REVERB_DECAY_TIME),
-    DECL(AL_REVERB_DECAY_HFRATIO),
-    DECL(AL_REVERB_REFLECTIONS_GAIN),
-    DECL(AL_REVERB_REFLECTIONS_DELAY),
-    DECL(AL_REVERB_LATE_REVERB_GAIN),
-    DECL(AL_REVERB_LATE_REVERB_DELAY),
-    DECL(AL_REVERB_AIR_ABSORPTION_GAINHF),
-    DECL(AL_REVERB_ROOM_ROLLOFF_FACTOR),
-    DECL(AL_REVERB_DECAY_HFLIMIT),
-
-    DECL(AL_CHORUS_WAVEFORM),
-    DECL(AL_CHORUS_PHASE),
-    DECL(AL_CHORUS_RATE),
-    DECL(AL_CHORUS_DEPTH),
-    DECL(AL_CHORUS_FEEDBACK),
-    DECL(AL_CHORUS_DELAY),
-
-    DECL(AL_DISTORTION_EDGE),
-    DECL(AL_DISTORTION_GAIN),
-    DECL(AL_DISTORTION_LOWPASS_CUTOFF),
-    DECL(AL_DISTORTION_EQCENTER),
-    DECL(AL_DISTORTION_EQBANDWIDTH),
-
-    DECL(AL_ECHO_DELAY),
-    DECL(AL_ECHO_LRDELAY),
-    DECL(AL_ECHO_DAMPING),
-    DECL(AL_ECHO_FEEDBACK),
-    DECL(AL_ECHO_SPREAD),
-
-    DECL(AL_FLANGER_WAVEFORM),
-    DECL(AL_FLANGER_PHASE),
-    DECL(AL_FLANGER_RATE),
-    DECL(AL_FLANGER_DEPTH),
-    DECL(AL_FLANGER_FEEDBACK),
-    DECL(AL_FLANGER_DELAY),
-
-    DECL(AL_RING_MODULATOR_FREQUENCY),
-    DECL(AL_RING_MODULATOR_HIGHPASS_CUTOFF),
-    DECL(AL_RING_MODULATOR_WAVEFORM),
-
-#if 0
-    DECL(AL_AUTOWAH_ATTACK_TIME),
-    DECL(AL_AUTOWAH_PEAK_GAIN),
-    DECL(AL_AUTOWAH_RELEASE_TIME),
-    DECL(AL_AUTOWAH_RESONANCE),
-#endif
-
-    DECL(AL_COMPRESSOR_ONOFF),
-
-    DECL(AL_EQUALIZER_LOW_GAIN),
-    DECL(AL_EQUALIZER_LOW_CUTOFF),
-    DECL(AL_EQUALIZER_MID1_GAIN),
-    DECL(AL_EQUALIZER_MID1_CENTER),
-    DECL(AL_EQUALIZER_MID1_WIDTH),
-    DECL(AL_EQUALIZER_MID2_GAIN),
-    DECL(AL_EQUALIZER_MID2_CENTER),
-    DECL(AL_EQUALIZER_MID2_WIDTH),
-    DECL(AL_EQUALIZER_HIGH_GAIN),
-    DECL(AL_EQUALIZER_HIGH_CUTOFF),
-
-    DECL(AL_DEDICATED_GAIN),
-
-    { NULL, (ALCenum)0 }
-};
-#undef DECL
-
-static const ALCchar alcNoError[] = "No Error";
-static const ALCchar alcErrInvalidDevice[] = "Invalid Device";
-static const ALCchar alcErrInvalidContext[] = "Invalid Context";
-static const ALCchar alcErrInvalidEnum[] = "Invalid Enum";
-static const ALCchar alcErrInvalidValue[] = "Invalid Value";
-static const ALCchar alcErrOutOfMemory[] = "Out of Memory";
-
-
-/************************************************
- * Global variables
- ************************************************/
-
-/* Enumerated device names */
-static const ALCchar alcDefaultName[] = "OpenAL Soft\0";
-
-static al_string alcAllDevicesList;
-static al_string alcCaptureDeviceList;
-
-/* Default is always the first in the list */
-static ALCchar *alcDefaultAllDevicesSpecifier;
-static ALCchar *alcCaptureDefaultDeviceSpecifier;
-
-/* Default context extensions */
-static const ALchar alExtList[] =
-    "AL_EXT_ALAW AL_EXT_DOUBLE AL_EXT_EXPONENT_DISTANCE AL_EXT_FLOAT32 "
-    "AL_EXT_IMA4 AL_EXT_LINEAR_DISTANCE AL_EXT_MCFORMATS AL_EXT_MULAW "
-    "AL_EXT_MULAW_MCFORMATS AL_EXT_OFFSET AL_EXT_source_distance_model "
-    "AL_LOKI_quadriphonic AL_SOFT_block_alignment AL_SOFT_buffer_samples "
-    "AL_SOFT_buffer_sub_data AL_SOFT_deferred_updates AL_SOFT_direct_channels "
-    "AL_SOFT_loop_points AL_SOFT_MSADPCM AL_SOFT_source_latency "
-    "AL_SOFT_source_length";
-
-static ATOMIC(ALCenum) LastNullDeviceError = ATOMIC_INIT_STATIC(ALC_NO_ERROR);
-
-/* Thread-local current context */
-static altss_t LocalContext;
-/* Process-wide current context */
-static ATOMIC(ALCcontext*) GlobalContext = ATOMIC_INIT_STATIC(NULL);
-
-/* Mixing thread piority level */
-ALint RTPrioLevel;
-
-FILE *LogFile;
-#ifdef _DEBUG
-enum LogLevel LogLevel = LogWarning;
-#else
-enum LogLevel LogLevel = LogError;
-#endif
-
-/* Flag to trap ALC device errors */
-static ALCboolean TrapALCError = ALC_FALSE;
-
-/* One-time configuration init control */
-static alonce_flag alc_config_once = AL_ONCE_FLAG_INIT;
-
-/* Default effect that applies to sources that don't have an effect on send 0 */
-static ALeffect DefaultEffect;
-
-
-/************************************************
- * ALC information
- ************************************************/
-static const ALCchar alcNoDeviceExtList[] =
-    "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
-    "ALC_EXT_thread_local_context ALC_SOFT_loopback";
-static const ALCchar alcExtensionList[] =
-    "ALC_ENUMERATE_ALL_EXT ALC_ENUMERATION_EXT ALC_EXT_CAPTURE "
-    "ALC_EXT_DEDICATED ALC_EXT_disconnect ALC_EXT_EFX "
-    "ALC_EXT_thread_local_context ALC_SOFTX_device_clock ALC_SOFTX_HRTF "
-    "ALC_SOFT_loopback ALC_SOFTX_midi_interface ALC_SOFT_pause_device";
-static const ALCint alcMajorVersion = 1;
-static const ALCint alcMinorVersion = 1;
-
-static const ALCint alcEFXMajorVersion = 1;
-static const ALCint alcEFXMinorVersion = 0;
-
-
-/************************************************
- * Device lists
- ************************************************/
-static ATOMIC(ALCdevice*) DeviceList = ATOMIC_INIT_STATIC(NULL);
-
-static almtx_t ListLock;
-static inline void LockLists(void)
-{
-    int lockret = almtx_lock(&ListLock);
-    assert(lockret == althrd_success);
-}
-static inline void UnlockLists(void)
-{
-    int unlockret = almtx_unlock(&ListLock);
-    assert(unlockret == althrd_success);
-}
-
-/************************************************
- * Library initialization
- ************************************************/
-#if defined(_WIN32)
-static void alc_init(void);
-static void alc_deinit(void);
-static void alc_deinit_safe(void);
-
-#ifndef AL_LIBTYPE_STATIC
-BOOL APIENTRY DllMain(HINSTANCE hModule, DWORD reason, LPVOID lpReserved)
-{
-    switch(reason)
-    {
-        case DLL_PROCESS_ATTACH:
-            /* Pin the DLL so we won't get unloaded until the process terminates */
-            GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN | GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
-                               (WCHAR*)hModule, &hModule);
-            alc_init();
-            break;
-
-        case DLL_THREAD_DETACH:
-            break;
-
-        case DLL_PROCESS_DETACH:
-            if(!lpReserved)
-                alc_deinit();
-            else
-                alc_deinit_safe();
-            break;
-    }
-    return TRUE;
-}
-#elif defined(_MSC_VER)
-#pragma section(".CRT$XCU",read)
-static void alc_constructor(void);
-static void alc_destructor(void);
-__declspec(allocate(".CRT$XCU")) void (__cdecl* alc_constructor_)(void) = alc_constructor;
-
-static void alc_constructor(void)
-{
-    atexit(alc_destructor);
-    alc_init();
-}
-
-static void alc_destructor(void)
-{
-    alc_deinit();
-}
-#elif defined(HAVE_GCC_DESTRUCTOR)
-static void alc_init(void) __attribute__((constructor));
-static void alc_deinit(void) __attribute__((destructor));
-#else
-#error "No static initialization available on this platform!"
-#endif
-
-#elif defined(HAVE_GCC_DESTRUCTOR)
-
-static void alc_init(void) __attribute__((constructor));
-static void alc_deinit(void) __attribute__((destructor));
-
-#else
-#error "No global initialization available on this platform!"
-#endif
-
-static void ReleaseThreadCtx(void *ptr);
-static void alc_init(void)
-{
-    const char *str;
-    int ret;
-
-    LogFile = stderr;
-
-    AL_STRING_INIT(alcAllDevicesList);
-    AL_STRING_INIT(alcCaptureDeviceList);
-
-    str = getenv("__ALSOFT_HALF_ANGLE_CONES");
-    if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
-        ConeScale *= 0.5f;
-
-    str = getenv("__ALSOFT_REVERSE_Z");
-    if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
-        ZScale *= -1.0f;
-
-    ret = altss_create(&LocalContext, ReleaseThreadCtx);
-    assert(ret == althrd_success);
-
-    ret = almtx_init(&ListLock, almtx_recursive);
-    assert(ret == althrd_success);
-
-    ThunkInit();
-}
-
-static void alc_initconfig(void)
-{
-    const char *devs, *str;
-    ALuint capfilter;
-    float valf;
-    int i, n;
-
-    str = getenv("ALSOFT_LOGLEVEL");
-    if(str)
-    {
-        long lvl = strtol(str, NULL, 0);
-        if(lvl >= NoLog && lvl <= LogRef)
-            LogLevel = lvl;
-    }
-
-    str = getenv("ALSOFT_LOGFILE");
-    if(str && str[0])
-    {
-        FILE *logfile = al_fopen(str, "wt");
-        if(logfile) LogFile = logfile;
-        else ERR("Failed to open log file '%s'\n", str);
-    }
-
-    {
-        char buf[1024] = "";
-        int len = snprintf(buf, sizeof(buf), "%s", BackendList[0].name);
-        for(i = 1;BackendList[i].name;i++)
-            len += snprintf(buf+len, sizeof(buf)-len, ", %s", BackendList[i].name);
-        TRACE("Supported backends: %s\n", buf);
-    }
-    ReadALConfig();
-
-    capfilter = 0;
-#if defined(HAVE_SSE4_1)
-    capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2 | CPU_CAP_SSE4_1;
-#elif defined(HAVE_SSE2)
-    capfilter |= CPU_CAP_SSE | CPU_CAP_SSE2;
-#elif defined(HAVE_SSE)
-    capfilter |= CPU_CAP_SSE;
-#endif
-#ifdef HAVE_NEON
-    capfilter |= CPU_CAP_NEON;
-#endif
-    if(ConfigValueStr(NULL, "disable-cpu-exts", &str))
-    {
-        if(strcasecmp(str, "all") == 0)
-            capfilter = 0;
-        else
-        {
-            size_t len;
-            const char *next = str;
-
-            do {
-                str = next;
-                while(isspace(str[0]))
-                    str++;
-                next = strchr(str, ',');
-
-                if(!str[0] || str[0] == ',')
-                    continue;
-
-                len = (next ? ((size_t)(next-str)) : strlen(str));
-                while(len > 0 && isspace(str[len-1]))
-                    len--;
-                if(len == 3 && strncasecmp(str, "sse", len) == 0)
-                    capfilter &= ~CPU_CAP_SSE;
-                else if(len == 4 && strncasecmp(str, "sse2", len) == 0)
-                    capfilter &= ~CPU_CAP_SSE2;
-                else if(len == 6 && strncasecmp(str, "sse4.1", len) == 0)
-                    capfilter &= ~CPU_CAP_SSE4_1;
-                else if(len == 4 && strncasecmp(str, "neon", len) == 0)
-                    capfilter &= ~CPU_CAP_NEON;
-                else
-                    WARN("Invalid CPU extension \"%s\"\n", str);
-            } while(next++);
-        }
-    }
-    FillCPUCaps(capfilter);
-
-#ifdef _WIN32
-    RTPrioLevel = 1;
-#else
-    RTPrioLevel = 0;
-#endif
-    ConfigValueInt(NULL, "rt-prio", &RTPrioLevel);
-
-    if(ConfigValueStr(NULL, "resampler", &str))
-    {
-        if(strcasecmp(str, "point") == 0 || strcasecmp(str, "none") == 0)
-            DefaultResampler = PointResampler;
-        else if(strcasecmp(str, "linear") == 0)
-            DefaultResampler = LinearResampler;
-        else if(strcasecmp(str, "cubic") == 0)
-            DefaultResampler = CubicResampler;
-        else
-        {
-            char *end;
-
-            n = strtol(str, &end, 0);
-            if(*end == '\0' && (n == PointResampler || n == LinearResampler || n == CubicResampler))
-                DefaultResampler = n;
-            else
-                WARN("Invalid resampler: %s\n", str);
-        }
-    }
-
-    str = getenv("ALSOFT_TRAP_ERROR");
-    if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
-    {
-        TrapALError  = AL_TRUE;
-        TrapALCError = AL_TRUE;
-    }
-    else
-    {
-        str = getenv("ALSOFT_TRAP_AL_ERROR");
-        if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
-            TrapALError = AL_TRUE;
-        TrapALError = GetConfigValueBool(NULL, "trap-al-error", TrapALError);
-
-        str = getenv("ALSOFT_TRAP_ALC_ERROR");
-        if(str && (strcasecmp(str, "true") == 0 || strtol(str, NULL, 0) == 1))
-            TrapALCError = ALC_TRUE;
-        TrapALCError = GetConfigValueBool(NULL, "trap-alc-error", TrapALCError);
-    }
-
-    if(ConfigValueFloat("reverb", "boost", &valf))
-        ReverbBoost *= powf(10.0f, valf / 20.0f);
-
-    EmulateEAXReverb = GetConfigValueBool("reverb", "emulate-eax", AL_FALSE);
-
-    if(((devs=getenv("ALSOFT_DRIVERS")) && devs[0]) ||
-       ConfigValueStr(NULL, "drivers", &devs))
-    {
-        int n;
-        size_t len;
-        const char *next = devs;
-        int endlist, delitem;
-
-        i = 0;
-        do {
-            devs = next;
-            while(isspace(devs[0]))
-                devs++;
-            next = strchr(devs, ',');
-
-            delitem = (devs[0] == '-');
-            if(devs[0] == '-') devs++;
-
-            if(!devs[0] || devs[0] == ',')
-            {
-                endlist = 0;
-                continue;
-            }
-            endlist = 1;
-
-            len = (next ? ((size_t)(next-devs)) : strlen(devs));
-            while(len > 0 && isspace(devs[len-1]))
-                len--;
-            for(n = i;BackendList[n].name;n++)
-            {
-                if(len == strlen(BackendList[n].name) &&
-                   strncmp(BackendList[n].name, devs, len) == 0)
-                {
-                    if(delitem)
-                    {
-                        do {
-                            BackendList[n] = BackendList[n+1];
-                            ++n;
-                        } while(BackendList[n].name);
-                    }
-                    else
-                    {
-                        struct BackendInfo Bkp = BackendList[n];
-                        while(n > i)
-                        {
-                            BackendList[n] = BackendList[n-1];
-                            --n;
-                        }
-                        BackendList[n] = Bkp;
-
-                        i++;
-                    }
-                    break;
-                }
-            }
-        } while(next++);
-
-        if(endlist)
-        {
-            BackendList[i].name = NULL;
-            BackendList[i].getFactory = NULL;
-            BackendList[i].Init = NULL;
-            BackendList[i].Deinit = NULL;
-            BackendList[i].Probe = NULL;
-        }
-    }
-
-    for(i = 0;(BackendList[i].Init || BackendList[i].getFactory) && (!PlaybackBackend.name || !CaptureBackend.name);i++)
-    {
-        if(BackendList[i].getFactory)
-        {
-            ALCbackendFactory *factory = BackendList[i].getFactory();
-            if(!V0(factory,init)())
-            {
-                WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
-                continue;
-            }
-
-            TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
-            if(!PlaybackBackend.name && V(factory,querySupport)(ALCbackend_Playback))
-            {
-                PlaybackBackend = BackendList[i];
-                TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
-            }
-            if(!CaptureBackend.name && V(factory,querySupport)(ALCbackend_Capture))
-            {
-                CaptureBackend = BackendList[i];
-                TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
-            }
-
-            continue;
-        }
-
-        if(!BackendList[i].Init(&BackendList[i].Funcs))
-        {
-            WARN("Failed to initialize backend \"%s\"\n", BackendList[i].name);
-            continue;
-        }
-
-        TRACE("Initialized backend \"%s\"\n", BackendList[i].name);
-        if(BackendList[i].Funcs.OpenPlayback && !PlaybackBackend.name)
-        {
-            PlaybackBackend = BackendList[i];
-            TRACE("Added \"%s\" for playback\n", PlaybackBackend.name);
-        }
-        if(BackendList[i].Funcs.OpenCapture && !CaptureBackend.name)
-        {
-            CaptureBackend = BackendList[i];
-            TRACE("Added \"%s\" for capture\n", CaptureBackend.name);
-        }
-    }
-    {
-        ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
-        V0(factory,init)();
-    }
-
-    if(ConfigValueStr(NULL, "excludefx", &str))
-    {
-        size_t len;
-        const char *next = str;
-
-        do {
-            str = next;
-            next = strchr(str, ',');
-
-            if(!str[0] || next == str)
-                continue;
-
-            len = (next ? ((size_t)(next-str)) : strlen(str));
-            for(n = 0;EffectList[n].name;n++)
-            {
-                if(len == strlen(EffectList[n].name) &&
-                   strncmp(EffectList[n].name, str, len) == 0)
-                    DisabledEffects[EffectList[n].type] = AL_TRUE;
-            }
-        } while(next++);
-    }
-
-    InitEffectFactoryMap();
-
-    InitEffect(&DefaultEffect);
-    str = getenv("ALSOFT_DEFAULT_REVERB");
-    if((str && str[0]) || ConfigValueStr(NULL, "default-reverb", &str))
-        LoadReverbPreset(str, &DefaultEffect);
-}
-#define DO_INITCONFIG() alcall_once(&alc_config_once, alc_initconfig)
-
-
-/************************************************
- * Library deinitialization
- ************************************************/
-static void alc_cleanup(void)
-{
-    ALCdevice *dev;
-
-    AL_STRING_DEINIT(alcAllDevicesList);
-    AL_STRING_DEINIT(alcCaptureDeviceList);
-
-    free(alcDefaultAllDevicesSpecifier);
-    alcDefaultAllDevicesSpecifier = NULL;
-    free(alcCaptureDefaultDeviceSpecifier);
-    alcCaptureDefaultDeviceSpecifier = NULL;
-
-    if((dev=ATOMIC_EXCHANGE(ALCdevice*, &DeviceList, NULL)) != NULL)
-    {
-        ALCuint num = 0;
-        do {
-            num++;
-        } while((dev=dev->next) != NULL);
-        ERR("%u device%s not closed\n", num, (num>1)?"s":"");
-    }
-
-    DeinitEffectFactoryMap();
-}
-
-static void alc_deinit_safe(void)
-{
-    alc_cleanup();
-
-    FreeHrtfs();
-    FreeALConfig();
-
-    ThunkExit();
-    almtx_destroy(&ListLock);
-    altss_delete(LocalContext);
-
-    if(LogFile != stderr)
-        fclose(LogFile);
-    LogFile = NULL;
-}
-
-static void alc_deinit(void)
-{
-    int i;
-
-    alc_cleanup();
-
-    memset(&PlaybackBackend, 0, sizeof(PlaybackBackend));
-    memset(&CaptureBackend, 0, sizeof(CaptureBackend));
-
-    for(i = 0;BackendList[i].Deinit || BackendList[i].getFactory;i++)
-    {
-        if(!BackendList[i].getFactory)
-            BackendList[i].Deinit();
-        else
-        {
-            ALCbackendFactory *factory = BackendList[i].getFactory();
-            V0(factory,deinit)();
-        }
-    }
-    {
-        ALCbackendFactory *factory = ALCloopbackFactory_getFactory();
-        V0(factory,deinit)();
-    }
-
-    alc_deinit_safe();
-}
-
-
-/************************************************
- * Device enumeration
- ************************************************/
-static void ProbeDevices(al_string *list, enum DevProbe type)
-{
-    DO_INITCONFIG();
-
-    LockLists();
-    al_string_clear(list);
-
-    if(type == ALL_DEVICE_PROBE && (PlaybackBackend.Probe || PlaybackBackend.getFactory))
-    {
-        if(!PlaybackBackend.getFactory)
-            PlaybackBackend.Probe(type);
-        else
-        {
-            ALCbackendFactory *factory = PlaybackBackend.getFactory();
-            V(factory,probe)(type);
-        }
-    }
-    else if(type == CAPTURE_DEVICE_PROBE && (CaptureBackend.Probe || CaptureBackend.getFactory))
-    {
-        if(!CaptureBackend.getFactory)
-            CaptureBackend.Probe(type);
-        else
-        {
-            ALCbackendFactory *factory = CaptureBackend.getFactory();
-            V(factory,probe)(type);
-        }
-    }
-    UnlockLists();
-}
-static void ProbeAllDevicesList(void)
-{ ProbeDevices(&alcAllDevicesList, ALL_DEVICE_PROBE); }
-static void ProbeCaptureDeviceList(void)
-{ ProbeDevices(&alcCaptureDeviceList, CAPTURE_DEVICE_PROBE); }
-
-static void AppendDevice(const ALCchar *name, al_string *devnames)
-{
-    size_t len = strlen(name);
-    if(len > 0)
-    {
-        al_string_append_range(devnames, name, name+len);
-        al_string_append_char(devnames, '\0');
-    }
-}
-void AppendAllDevicesList(const ALCchar *name)
-{ AppendDevice(name, &alcAllDevicesList); }
-void AppendCaptureDeviceList(const ALCchar *name)
-{ AppendDevice(name, &alcCaptureDeviceList); }
-
-
-/************************************************
- * Device format information
- ************************************************/
-const ALCchar *DevFmtTypeString(enum DevFmtType type)
-{
-    switch(type)
-    {
-    case DevFmtByte: return "Signed Byte";
-    case DevFmtUByte: return "Unsigned Byte";
-    case DevFmtShort: return "Signed Short";
-    case DevFmtUShort: return "Unsigned Short";
-    case DevFmtInt: return "Signed Int";
-    case DevFmtUInt: return "Unsigned Int";
-    case DevFmtFloat: return "Float";
-    }
-    return "(unknown type)";
-}
-const ALCchar *DevFmtChannelsString(enum DevFmtChannels chans)
-{
-    switch(chans)
-    {
-    case DevFmtMono: return "Mono";
-    case DevFmtStereo: return "Stereo";
-    case DevFmtQuad: return "Quadraphonic";
-    case DevFmtX51: return "5.1 Surround";
-    case DevFmtX51Side: return "5.1 Side";
-    case DevFmtX61: return "6.1 Surround";
-    case DevFmtX71: return "7.1 Surround";
-    }
-    return "(unknown channels)";
-}
-
-extern inline ALuint FrameSizeFromDevFmt(enum DevFmtChannels chans, enum DevFmtType type);
-ALuint BytesFromDevFmt(enum DevFmtType type)
-{
-    switch(type)
-    {
-    case DevFmtByte: return sizeof(ALbyte);
-    case DevFmtUByte: return sizeof(ALubyte);
-    case DevFmtShort: return sizeof(ALshort);
-    case DevFmtUShort: return sizeof(ALushort);
-    case DevFmtInt: return sizeof(ALint);
-    case DevFmtUInt: return sizeof(ALuint);
-    case DevFmtFloat: return sizeof(ALfloat);
-    }
-    return 0;
-}
-ALuint ChannelsFromDevFmt(enum DevFmtChannels chans)
-{
-    switch(chans)
-    {
-    case DevFmtMono: return 1;
-    case DevFmtStereo: return 2;
-    case DevFmtQuad: return 4;
-    case DevFmtX51: return 6;
-    case DevFmtX51Side: return 6;
-    case DevFmtX61: return 7;
-    case DevFmtX71: return 8;
-    }
-    return 0;
-}
-
-DECL_CONST static ALboolean DecomposeDevFormat(ALenum format,
-  enum DevFmtChannels *chans, enum DevFmtType *type)
-{
-    static const struct {
-        ALenum format;
-        enum DevFmtChannels channels;
-        enum DevFmtType type;
-    } list[] = {
-        { AL_FORMAT_MONO8,        DevFmtMono, DevFmtUByte },
-        { AL_FORMAT_MONO16,       DevFmtMono, DevFmtShort },
-        { AL_FORMAT_MONO_FLOAT32, DevFmtMono, DevFmtFloat },
-
-        { AL_FORMAT_STEREO8,        DevFmtStereo, DevFmtUByte },
-        { AL_FORMAT_STEREO16,       DevFmtStereo, DevFmtShort },
-        { AL_FORMAT_STEREO_FLOAT32, DevFmtStereo, DevFmtFloat },
-
-        { AL_FORMAT_QUAD8,  DevFmtQuad, DevFmtUByte },
-        { AL_FORMAT_QUAD16, DevFmtQuad, DevFmtShort },
-        { AL_FORMAT_QUAD32, DevFmtQuad, DevFmtFloat },
-
-        { AL_FORMAT_51CHN8,  DevFmtX51, DevFmtUByte },
-        { AL_FORMAT_51CHN16, DevFmtX51, DevFmtShort },
-        { AL_FORMAT_51CHN32, DevFmtX51, DevFmtFloat },
-
-        { AL_FORMAT_61CHN8,  DevFmtX61, DevFmtUByte },
-        { AL_FORMAT_61CHN16, DevFmtX61, DevFmtShort },
-        { AL_FORMAT_61CHN32, DevFmtX61, DevFmtFloat },
-
-        { AL_FORMAT_71CHN8,  DevFmtX71, DevFmtUByte },
-        { AL_FORMAT_71CHN16, DevFmtX71, DevFmtShort },
-        { AL_FORMAT_71CHN32, DevFmtX71, DevFmtFloat },
-    };
-    ALuint i;
-
-    for(i = 0;i < COUNTOF(list);i++)
-    {
-        if(list[i].format == format)
-        {
-            *chans = list[i].channels;
-            *type  = list[i].type;
-            return AL_TRUE;
-        }
-    }
-
-    return AL_FALSE;
-}
-
-DECL_CONST static ALCboolean IsValidALCType(ALCenum type)
-{
-    switch(type)
-    {
-        case ALC_BYTE_SOFT:
-        case ALC_UNSIGNED_BYTE_SOFT:
-        case ALC_SHORT_SOFT:
-        case ALC_UNSIGNED_SHORT_SOFT:
-        case ALC_INT_SOFT:
-        case ALC_UNSIGNED_INT_SOFT:
-        case ALC_FLOAT_SOFT:
-            return ALC_TRUE;
-    }
-    return ALC_FALSE;
-}
-
-DECL_CONST static ALCboolean IsValidALCChannels(ALCenum channels)
-{
-    switch(channels)
-    {
-        case ALC_MONO_SOFT:
-        case ALC_STEREO_SOFT:
-        case ALC_QUAD_SOFT:
-        case ALC_5POINT1_SOFT:
-        case ALC_6POINT1_SOFT:
-        case ALC_7POINT1_SOFT:
-            return ALC_TRUE;
-    }
-    return ALC_FALSE;
-}
-
-
-/************************************************
- * Miscellaneous ALC helpers
- ************************************************/
-extern inline void LockContext(ALCcontext *context);
-extern inline void UnlockContext(ALCcontext *context);
-
-ALint64 ALCdevice_GetLatencyDefault(ALCdevice *UNUSED(device))
-{
-    return 0;
-}
-
-ALint64 ALCdevice_GetLatency(ALCdevice *device)
-{
-    return V0(device->Backend,getLatency)();
-}
-
-void ALCdevice_Lock(ALCdevice *device)
-{
-    V0(device->Backend,lock)();
-}
-
-void ALCdevice_Unlock(ALCdevice *device)
-{
-    V0(device->Backend,unlock)();
-}
-
-
-/* SetDefaultWFXChannelOrder
- *
- * Sets the default channel order used by WaveFormatEx.
- */
-void SetDefaultWFXChannelOrder(ALCdevice *device)
-{
-    ALuint i;
-
-    for(i = 0;i < MaxChannels;i++)
-        device->ChannelOffsets[i] = INVALID_OFFSET;
-
-    switch(device->FmtChans)
-    {
-    case DevFmtMono: device->ChannelOffsets[FrontCenter] = 0;
-                     break;
-    case DevFmtStereo: device->ChannelOffsets[FrontLeft]  = 0;
-                       device->ChannelOffsets[FrontRight] = 1;
-                       break;
-    case DevFmtQuad: device->ChannelOffsets[FrontLeft]  = 0;
-                     device->ChannelOffsets[FrontRight] = 1;
-                     device->ChannelOffsets[BackLeft]   = 2;
-                     device->ChannelOffsets[BackRight]  = 3;
-                     break;
-    case DevFmtX51: device->ChannelOffsets[FrontLeft]   = 0;
-                    device->ChannelOffsets[FrontRight]  = 1;
-                    device->ChannelOffsets[FrontCenter] = 2;
-                    device->ChannelOffsets[LFE]         = 3;
-                    device->ChannelOffsets[BackLeft]    = 4;
-                    device->ChannelOffsets[BackRight]   = 5;
-                    break;
-    case DevFmtX51Side: device->ChannelOffsets[FrontLeft]   = 0;
-                        device->ChannelOffsets[FrontRight]  = 1;
-                        device->ChannelOffsets[FrontCenter] = 2;
-                        device->ChannelOffsets[LFE]         = 3;
-                        device->ChannelOffsets[SideLeft]    = 4;
-                        device->ChannelOffsets[SideRight]   = 5;
-                        break;
-    case DevFmtX61: device->ChannelOffsets[FrontLeft]   = 0;
-                    device->ChannelOffsets[FrontRight]  = 1;
-                    device->ChannelOffsets[FrontCenter] = 2;
-                    device->ChannelOffsets[LFE]         = 3;
-                    device->ChannelOffsets[BackCenter]  = 4;
-                    device->ChannelOffsets[SideLeft]    = 5;
-                    device->ChannelOffsets[SideRight]   = 6;
-                    break;
-    case DevFmtX71: device->ChannelOffsets[FrontLeft]   = 0;
-                    device->ChannelOffsets[FrontRight]  = 1;
-                    device->ChannelOffsets[FrontCenter] = 2;
-                    device->ChannelOffsets[LFE]         = 3;
-                    device->ChannelOffsets[BackLeft]    = 4;
-                    device->ChannelOffsets[BackRight]   = 5;
-                    device->ChannelOffsets[SideLeft]    = 6;
-                    device->ChannelOffsets[SideRight]   = 7;
-                    break;
-    }
-}
-
-/* SetDefaultChannelOrder
- *
- * Sets the default channel order used by most non-WaveFormatEx-based APIs.
- */
-void SetDefaultChannelOrder(ALCdevice *device)
-{
-    ALuint i;
-
-    for(i = 0;i < MaxChannels;i++)
-        device->ChannelOffsets[i] = INVALID_OFFSET;
-
-    switch(device->FmtChans)
-    {
-    case DevFmtX51: device->ChannelOffsets[FrontLeft]   = 0;
-                    device->ChannelOffsets[FrontRight]  = 1;
-                    device->ChannelOffsets[BackLeft]    = 2;
-                    device->ChannelOffsets[BackRight]   = 3;
-                    device->ChannelOffsets[FrontCenter] = 4;
-                    device->ChannelOffsets[LFE]         = 5;
-                    return;
-    case DevFmtX71: device->ChannelOffsets[FrontLeft]   = 0;
-                    device->ChannelOffsets[FrontRight]  = 1;
-                    device->ChannelOffsets[BackLeft]    = 2;
-                    device->ChannelOffsets[BackRight]   = 3;
-                    device->ChannelOffsets[FrontCenter] = 4;
-                    device->ChannelOffsets[LFE]         = 5;
-                    device->ChannelOffsets[SideLeft]    = 6;
-                    device->ChannelOffsets[SideRight]   = 7;
-                    return;
-
-    /* Same as WFX order */
-    case DevFmtMono:
-    case DevFmtStereo:
-    case DevFmtQuad:
-    case DevFmtX51Side:
-    case DevFmtX61:
-        break;
-    }
-    SetDefaultWFXChannelOrder(device);
-}
-
-
-/* alcSetError
- *
- * Stores the latest ALC device error
- */
-static void alcSetError(ALCdevice *device, ALCenum errorCode)
-{
-    if(TrapALCError)
-    {
-#ifdef _WIN32
-        /* DebugBreak() will cause an exception if there is no debugger */
-        if(IsDebuggerPresent())
-            DebugBreak();
-#elif defined(SIGTRAP)
-        raise(SIGTRAP);
-#endif
-    }
-
-    if(device)
-        ATOMIC_STORE(&device->LastError, errorCode);
-    else
-        ATOMIC_STORE(&LastNullDeviceError, errorCode);
-}
-
-
-/* UpdateClockBase
- *
- * Updates the device's base clock time with however many samples have been
- * done. This is used so frequency changes on the device don't cause the time
- * to jump forward or back.
- */
-static inline void UpdateClockBase(ALCdevice *device)
-{
-    device->ClockBase += device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency;
-    device->SamplesDone = 0;
-}
-
-/* UpdateDeviceParams
- *
- * Updates device parameters according to the attribute list (caller is
- * responsible for holding the list lock).
- */
-static ALCenum UpdateDeviceParams(ALCdevice *device, const ALCint *attrList)
-{
-    ALCcontext *context;
-    enum DevFmtChannels oldChans;
-    enum DevFmtType oldType;
-    ALCuint oldFreq;
-    FPUCtl oldMode;
-
-    // Check for attributes
-    if(device->Type == Loopback)
-    {
-        enum {
-            GotFreq  = 1<<0,
-            GotChans = 1<<1,
-            GotType  = 1<<2,
-            GotAll   = GotFreq|GotChans|GotType
-        };
-        ALCuint freq, numMono, numStereo, numSends, flags;
-        enum DevFmtChannels schans;
-        enum DevFmtType stype;
-        ALCuint attrIdx = 0;
-        ALCint gotFmt = 0;
-
-        if(!attrList)
-        {
-            WARN("Missing attributes for loopback device\n");
-            return ALC_INVALID_VALUE;
-        }
-
-        numMono = device->NumMonoSources;
-        numStereo = device->NumStereoSources;
-        numSends = device->NumAuxSends;
-        schans = device->FmtChans;
-        stype = device->FmtType;
-        freq = device->Frequency;
-        flags = device->Flags;
-
-        while(attrList[attrIdx])
-        {
-            if(attrList[attrIdx] == ALC_FORMAT_CHANNELS_SOFT)
-            {
-                ALCint val = attrList[attrIdx + 1];
-                if(!IsValidALCChannels(val) || !ChannelsFromDevFmt(val))
-                    return ALC_INVALID_VALUE;
-                schans = val;
-                gotFmt |= GotChans;
-            }
-
-            if(attrList[attrIdx] == ALC_FORMAT_TYPE_SOFT)
-            {
-                ALCint val = attrList[attrIdx + 1];
-                if(!IsValidALCType(val) || !BytesFromDevFmt(val))
-                    return ALC_INVALID_VALUE;
-                stype = val;
-                gotFmt |= GotType;
-            }
-
-            if(attrList[attrIdx] == ALC_FREQUENCY)
-            {
-                freq = attrList[attrIdx + 1];
-                if(freq < MIN_OUTPUT_RATE)
-                    return ALC_INVALID_VALUE;
-                gotFmt |= GotFreq;
-            }
-
-            if(attrList[attrIdx] == ALC_STEREO_SOURCES)
-            {
-                numStereo = attrList[attrIdx + 1];
-                if(numStereo > device->MaxNoOfSources)
-                    numStereo = device->MaxNoOfSources;
-
-                numMono = device->MaxNoOfSources - numStereo;
-            }
-
-            if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS)
-                numSends = attrList[attrIdx + 1];
-
-            if(attrList[attrIdx] == ALC_HRTF_SOFT)
-            {
-                if(attrList[attrIdx + 1] != ALC_FALSE)
-                    flags |= DEVICE_HRTF_REQUEST;
-                else
-                    flags &= ~DEVICE_HRTF_REQUEST;
-            }
-
-            attrIdx += 2;
-        }
-
-        if(gotFmt != GotAll)
-        {
-            WARN("Missing format for loopback device\n");
-            return ALC_INVALID_VALUE;
-        }
-
-        ConfigValueUInt(NULL, "sends", &numSends);
-        numSends = minu(MAX_SENDS, numSends);
-
-        if((device->Flags&DEVICE_RUNNING))
-            V0(device->Backend,stop)();
-        device->Flags = (flags & ~DEVICE_RUNNING);
-
-        UpdateClockBase(device);
-
-        device->Frequency = freq;
-        device->FmtChans = schans;
-        device->FmtType = stype;
-        device->NumMonoSources = numMono;
-        device->NumStereoSources = numStereo;
-        device->NumAuxSends = numSends;
-    }
-    else if(attrList && attrList[0])
-    {
-        ALCuint freq, numMono, numStereo, numSends;
-        ALCuint attrIdx = 0;
-
-        /* If a context is already running on the device, stop playback so the
-         * device attributes can be updated. */
-        if((device->Flags&DEVICE_RUNNING))
-            V0(device->Backend,stop)();
-        device->Flags &= ~DEVICE_RUNNING;
-
-        freq = device->Frequency;
-        numMono = device->NumMonoSources;
-        numStereo = device->NumStereoSources;
-        numSends = device->NumAuxSends;
-
-        while(attrList[attrIdx])
-        {
-            if(attrList[attrIdx] == ALC_FREQUENCY)
-            {
-                freq = attrList[attrIdx + 1];
-                device->Flags |= DEVICE_FREQUENCY_REQUEST;
-            }
-
-            if(attrList[attrIdx] == ALC_STEREO_SOURCES)
-            {
-                numStereo = attrList[attrIdx + 1];
-                if(numStereo > device->MaxNoOfSources)
-                    numStereo = device->MaxNoOfSources;
-
-                numMono = device->MaxNoOfSources - numStereo;
-            }
-
-            if(attrList[attrIdx] == ALC_MAX_AUXILIARY_SENDS)
-                numSends = attrList[attrIdx + 1];
-
-            if(attrList[attrIdx] == ALC_HRTF_SOFT)
-            {
-                if(attrList[attrIdx + 1] != ALC_FALSE)
-                    device->Flags |= DEVICE_HRTF_REQUEST;
-                else
-                    device->Flags &= ~DEVICE_HRTF_REQUEST;
-            }
-
-            attrIdx += 2;
-        }
-
-        ConfigValueUInt(NULL, "frequency", &freq);
-        freq = maxu(freq, MIN_OUTPUT_RATE);
-
-        ConfigValueUInt(NULL, "sends", &numSends);
-        numSends = minu(MAX_SENDS, numSends);
-
-        UpdateClockBase(device);
-
-        device->UpdateSize = (ALuint64)device->UpdateSize * freq /
-                             device->Frequency;
-        /* SSE and Neon do best with the update size being a multiple of 4 */
-        if((CPUCapFlags&(CPU_CAP_SSE|CPU_CAP_NEON)) != 0)
-            device->UpdateSize = (device->UpdateSize+3)&~3;
-
-        device->Frequency = freq;
-        device->NumMonoSources = numMono;
-        device->NumStereoSources = numStereo;
-        device->NumAuxSends = numSends;
-    }
-
-    if((device->Flags&DEVICE_RUNNING))
-        return ALC_NO_ERROR;
-
-    UpdateClockBase(device);
-
-    if(device->Type != Loopback)
-    {
-        bool usehrtf = !!(device->Flags&DEVICE_HRTF_REQUEST);
-        if(GetConfigValueBool(NULL, "hrtf", usehrtf))
-            device->Flags |= DEVICE_HRTF_REQUEST;
-        else
-            device->Flags &= ~DEVICE_HRTF_REQUEST;
-    }
-    if((device->Flags&DEVICE_HRTF_REQUEST))
-    {
-        enum DevFmtChannels chans = device->FmtChans;
-        ALCuint freq = device->Frequency;
-        if(FindHrtfFormat(&chans, &freq))
-        {
-            if(device->Type != Loopback)
-            {
-                device->Frequency = freq;
-                device->FmtChans = chans;
-                device->Flags |= DEVICE_CHANNELS_REQUEST |
-                                 DEVICE_FREQUENCY_REQUEST;
-            }
-            else if(device->Frequency != freq || device->FmtChans != chans)
-            {
-                ERR("Requested format not HRTF compatible: %s, %uhz\n",
-                    DevFmtChannelsString(device->FmtChans), device->Frequency);
-                device->Flags &= ~DEVICE_HRTF_REQUEST;
-            }
-        }
-    }
-
-    oldFreq  = device->Frequency;
-    oldChans = device->FmtChans;
-    oldType  = device->FmtType;
-
-    TRACE("Pre-reset: %s%s, %s%s, %s%uhz, %u update size x%d\n",
-          (device->Flags&DEVICE_CHANNELS_REQUEST)?"*":"",
-          DevFmtChannelsString(device->FmtChans),
-          (device->Flags&DEVICE_SAMPLE_TYPE_REQUEST)?"*":"",
-          DevFmtTypeString(device->FmtType),
-          (device->Flags&DEVICE_FREQUENCY_REQUEST)?"*":"",
-          device->Frequency,
-          device->UpdateSize, device->NumUpdates);
-
-    if(V0(device->Backend,reset)() == ALC_FALSE)
-        return ALC_INVALID_DEVICE;
-
-    if(device->FmtChans != oldChans && (device->Flags&DEVICE_CHANNELS_REQUEST))
-    {
-        ERR("Failed to set %s, got %s instead\n", DevFmtChannelsString(oldChans),
-            DevFmtChannelsString(device->FmtChans));
-        device->Flags &= ~DEVICE_CHANNELS_REQUEST;
-    }
-    if(device->FmtType != oldType && (device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
-    {
-        ERR("Failed to set %s, got %s instead\n", DevFmtTypeString(oldType),
-            DevFmtTypeString(device->FmtType));
-        device->Flags &= ~DEVICE_SAMPLE_TYPE_REQUEST;
-    }
-    if(device->Frequency != oldFreq && (device->Flags&DEVICE_FREQUENCY_REQUEST))
-    {
-        ERR("Failed to set %uhz, got %uhz instead\n", oldFreq, device->Frequency);
-        device->Flags &= ~DEVICE_FREQUENCY_REQUEST;
-    }
-
-    TRACE("Post-reset: %s, %s, %uhz, %u update size x%d\n",
-          DevFmtChannelsString(device->FmtChans),
-          DevFmtTypeString(device->FmtType), device->Frequency,
-          device->UpdateSize, device->NumUpdates);
-
-    aluInitPanning(device);
-
-    V(device->Synth,update)(device);
-
-    device->Hrtf = NULL;
-    if((device->Flags&DEVICE_HRTF_REQUEST))
-    {
-        device->Hrtf = GetHrtf(device->FmtChans, device->Frequency);
-        if(!device->Hrtf)
-            device->Flags &= ~DEVICE_HRTF_REQUEST;
-    }
-    TRACE("HRTF %s\n", device->Hrtf?"enabled":"disabled");
-
-    if(!device->Hrtf && device->Bs2bLevel > 0 && device->Bs2bLevel <= 6)
-    {
-        if(!device->Bs2b)
-        {
-            device->Bs2b = calloc(1, sizeof(*device->Bs2b));
-            bs2b_clear(device->Bs2b);
-        }
-        bs2b_set_srate(device->Bs2b, device->Frequency);
-        bs2b_set_level(device->Bs2b, device->Bs2bLevel);
-        TRACE("BS2B level %d\n", device->Bs2bLevel);
-    }
-    else
-    {
-        free(device->Bs2b);
-        device->Bs2b = NULL;
-        TRACE("BS2B disabled\n");
-    }
-
-    device->Flags &= ~DEVICE_WIDE_STEREO;
-    if(device->Type != Loopback && !device->Hrtf && GetConfigValueBool(NULL, "wide-stereo", AL_FALSE))
-        device->Flags |= DEVICE_WIDE_STEREO;
-
-    if(!device->Hrtf && (device->UpdateSize&3))
-    {
-        if((CPUCapFlags&CPU_CAP_SSE))
-            WARN("SSE performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
-        if((CPUCapFlags&CPU_CAP_NEON))
-            WARN("NEON performs best with multiple of 4 update sizes (%u)\n", device->UpdateSize);
-    }
-
-    SetMixerFPUMode(&oldMode);
-    ALCdevice_Lock(device);
-    context = ATOMIC_LOAD(&device->ContextList);
-    while(context)
-    {
-        ALsizei pos;
-
-        ATOMIC_STORE(&context->UpdateSources, AL_FALSE);
-        LockUIntMapRead(&context->EffectSlotMap);
-        for(pos = 0;pos < context->EffectSlotMap.size;pos++)
-        {
-            ALeffectslot *slot = context->EffectSlotMap.array[pos].value;
-
-            if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE)
-            {
-                UnlockUIntMapRead(&context->EffectSlotMap);
-                ALCdevice_Unlock(device);
-                RestoreFPUMode(&oldMode);
-                return ALC_INVALID_DEVICE;
-            }
-            ATOMIC_STORE(&slot->NeedsUpdate, AL_FALSE);
-            V(slot->EffectState,update)(device, slot);
-        }
-        UnlockUIntMapRead(&context->EffectSlotMap);
-
-        LockUIntMapRead(&context->SourceMap);
-        for(pos = 0;pos < context->SourceMap.size;pos++)
-        {
-            ALsource *source = context->SourceMap.array[pos].value;
-            ALuint s = device->NumAuxSends;
-            while(s < MAX_SENDS)
-            {
-                if(source->Send[s].Slot)
-                    DecrementRef(&source->Send[s].Slot->ref);
-                source->Send[s].Slot = NULL;
-                source->Send[s].Gain = 1.0f;
-                source->Send[s].GainHF = 1.0f;
-                s++;
-            }
-            ATOMIC_STORE(&source->NeedsUpdate, AL_TRUE);
-        }
-        UnlockUIntMapRead(&context->SourceMap);
-
-        for(pos = 0;pos < context->VoiceCount;pos++)
-        {
-            ALvoice *voice = &context->Voices[pos];
-            ALsource *source = voice->Source;
-            ALuint s = device->NumAuxSends;
-
-            while(s < MAX_SENDS)
-            {
-                voice->Send[s].Moving = AL_FALSE;
-                voice->Send[s].Counter = 0;
-                s++;
-            }
-
-            if(source)
-            {
-                ATOMIC_STORE(&source->NeedsUpdate, AL_FALSE);
-                voice->Update(voice, source, context);
-            }
-        }
-
-        context = context->next;
-    }
-    if(device->DefaultSlot)
-    {
-        ALeffectslot *slot = device->DefaultSlot;
-
-        if(V(slot->EffectState,deviceUpdate)(device) == AL_FALSE)
-        {
-            ALCdevice_Unlock(device);
-            RestoreFPUMode(&oldMode);
-            return ALC_INVALID_DEVICE;
-        }
-        ATOMIC_STORE(&slot->NeedsUpdate, AL_FALSE);
-        V(slot->EffectState,update)(device, slot);
-    }
-    ALCdevice_Unlock(device);
-    RestoreFPUMode(&oldMode);
-
-    if(!(device->Flags&DEVICE_PAUSED))
-    {
-        if(V0(device->Backend,start)() == ALC_FALSE)
-            return ALC_INVALID_DEVICE;
-        device->Flags |= DEVICE_RUNNING;
-    }
-
-    return ALC_NO_ERROR;
-}
-
-/* FreeDevice
- *
- * Frees the device structure, and destroys any objects the app failed to
- * delete. Called once there's no more references on the device.
- */
-static ALCvoid FreeDevice(ALCdevice *device)
-{
-    TRACE("%p\n", device);
-
-    V0(device->Backend,close)();
-    DELETE_OBJ(device->Backend);
-    device->Backend = NULL;
-
-    DELETE_OBJ(device->Synth);
-    device->Synth = NULL;
-
-    if(device->DefaultSlot)
-    {
-        ALeffectState *state = device->DefaultSlot->EffectState;
-        device->DefaultSlot = NULL;
-        DELETE_OBJ(state);
-    }
-
-    if(device->DefaultSfont)
-        ALsoundfont_deleteSoundfont(device->DefaultSfont, device);
-    device->DefaultSfont = NULL;
-
-    if(device->BufferMap.size > 0)
-    {
-        WARN("(%p) Deleting %d Buffer(s)\n", device, device->BufferMap.size);
-        ReleaseALBuffers(device);
-    }
-    ResetUIntMap(&device->BufferMap);
-
-    if(device->EffectMap.size > 0)
-    {
-        WARN("(%p) Deleting %d Effect(s)\n", device, device->EffectMap.size);
-        ReleaseALEffects(device);
-    }
-    ResetUIntMap(&device->EffectMap);
-
-    if(device->FilterMap.size > 0)
-    {
-        WARN("(%p) Deleting %d Filter(s)\n", device, device->FilterMap.size);
-        ReleaseALFilters(device);
-    }
-    ResetUIntMap(&device->FilterMap);
-
-    if(device->SfontMap.size > 0)
-    {
-        WARN("(%p) Deleting %d Soundfont(s)\n", device, device->SfontMap.size);
-        ReleaseALSoundfonts(device);
-    }
-    ResetUIntMap(&device->SfontMap);
-
-    if(device->PresetMap.size > 0)
-    {
-        WARN("(%p) Deleting %d Preset(s)\n", device, device->PresetMap.size);
-        ReleaseALPresets(device);
-    }
-    ResetUIntMap(&device->PresetMap);
-
-    if(device->FontsoundMap.size > 0)
-    {
-        WARN("(%p) Deleting %d Fontsound(s)\n", device, device->FontsoundMap.size);
-        ReleaseALFontsounds(device);
-    }
-    ResetUIntMap(&device->FontsoundMap);
-
-    free(device->Bs2b);
-    device->Bs2b = NULL;
-
-    AL_STRING_DEINIT(device->DeviceName);
-
-    al_free(device);
-}
-
-
-void ALCdevice_IncRef(ALCdevice *device)
-{
-    uint ref;
-    ref = IncrementRef(&device->ref);
-    TRACEREF("%p increasing refcount to %u\n", device, ref);
-}
-
-void ALCdevice_DecRef(ALCdevice *device)
-{
-    uint ref;
-    ref = DecrementRef(&device->ref);
-    TRACEREF("%p decreasing refcount to %u\n", device, ref);
-    if(ref == 0) FreeDevice(device);
-}
-
-/* VerifyDevice
- *
- * Checks if the device handle is valid, and increments its ref count if so.
- */
-static ALCdevice *VerifyDevice(ALCdevice *device)
-{
-    ALCdevice *tmpDevice;
-
-    if(!device)
-        return NULL;
-
-    LockLists();
-    tmpDevice = ATOMIC_LOAD(&DeviceList);
-    while(tmpDevice && tmpDevice != device)
-        tmpDevice = tmpDevice->next;
-
-    if(tmpDevice)
-        ALCdevice_IncRef(tmpDevice);
-    UnlockLists();
-    return tmpDevice;
-}
-
-
-/* InitContext
- *
- * Initializes context fields
- */
-static ALvoid InitContext(ALCcontext *Context)
-{
-    ALint i, j;
-
-    //Initialise listener
-    Context->Listener->Gain = 1.0f;
-    Context->Listener->MetersPerUnit = 1.0f;
-    Context->Listener->Position[0] = 0.0f;
-    Context->Listener->Position[1] = 0.0f;
-    Context->Listener->Position[2] = 0.0f;
-    Context->Listener->Velocity[0] = 0.0f;
-    Context->Listener->Velocity[1] = 0.0f;
-    Context->Listener->Velocity[2] = 0.0f;
-    Context->Listener->Forward[0] = 0.0f;
-    Context->Listener->Forward[1] = 0.0f;
-    Context->Listener->Forward[2] = -1.0f;
-    Context->Listener->Up[0] = 0.0f;
-    Context->Listener->Up[1] = 1.0f;
-    Context->Listener->Up[2] = 0.0f;
-    for(i = 0;i < 4;i++)
-    {
-        for(j = 0;j < 4;j++)
-            Context->Listener->Params.Matrix[i][j] = ((i==j) ? 1.0f : 0.0f);
-    }
-    for(i = 0;i < 3;i++)
-        Context->Listener->Params.Velocity[i] = 0.0f;
-
-    //Validate Context
-    ATOMIC_INIT(&Context->LastError, AL_NO_ERROR);
-    ATOMIC_INIT(&Context->UpdateSources, AL_FALSE);
-    InitUIntMap(&Context->SourceMap, Context->Device->MaxNoOfSources);
-    InitUIntMap(&Context->EffectSlotMap, Context->Device->AuxiliaryEffectSlotMax);
-
-    //Set globals
-    Context->DistanceModel = DefaultDistanceModel;
-    Context->SourceDistanceModel = AL_FALSE;
-    Context->DopplerFactor = 1.0f;
-    Context->DopplerVelocity = 1.0f;
-    Context->SpeedOfSound = SPEEDOFSOUNDMETRESPERSEC;
-    Context->DeferUpdates = AL_FALSE;
-
-    Context->ExtensionList = alExtList;
-}
-
-
-/* FreeContext
- *
- * Cleans up the context, and destroys any remaining objects the app failed to
- * delete. Called once there's no more references on the context.
- */
-static void FreeContext(ALCcontext *context)
-{
-    TRACE("%p\n", context);
-
-    if(context->SourceMap.size > 0)
-    {
-        WARN("(%p) Deleting %d Source(s)\n", context, context->SourceMap.size);
-        ReleaseALSources(context);
-    }
-    ResetUIntMap(&context->SourceMap);
-
-    if(context->EffectSlotMap.size > 0)
-    {
-        WARN("(%p) Deleting %d AuxiliaryEffectSlot(s)\n", context, context->EffectSlotMap.size);
-        ReleaseALAuxiliaryEffectSlots(context);
-    }
-    ResetUIntMap(&context->EffectSlotMap);
-
-    al_free(context->Voices);
-    context->Voices = NULL;
-    context->VoiceCount = 0;
-    context->MaxVoices = 0;
-
-    VECTOR_DEINIT(context->ActiveAuxSlots);
-
-    ALCdevice_DecRef(context->Device);
-    context->Device = NULL;
-
-    //Invalidate context
-    memset(context, 0, sizeof(ALCcontext));
-    al_free(context);
-}
-
-/* ReleaseContext
- *
- * Removes the context reference from the given device and removes it from
- * being current on the running thread or globally.
- */
-static void ReleaseContext(ALCcontext *context, ALCdevice *device)
-{
-    ALCcontext *nextctx;
-    ALCcontext *origctx;
-
-    if(altss_get(LocalContext) == context)
-    {
-        WARN("%p released while current on thread\n", context);
-        altss_set(LocalContext, NULL);
-        ALCcontext_DecRef(context);
-    }
-
-    origctx = context;
-    if(ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &GlobalContext, &origctx, NULL))
-        ALCcontext_DecRef(context);
-
-    ALCdevice_Lock(device);
-    origctx = context;
-    nextctx = context->next;
-    if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCcontext*, &device->ContextList, &origctx, nextctx))
-    {
-        ALCcontext *list;
-        do {
-            list = origctx;
-            origctx = context;
-        } while(!COMPARE_EXCHANGE(&list->next, &origctx, nextctx));
-    }
-    ALCdevice_Unlock(device);
-
-    ALCcontext_DecRef(context);
-}
-
-void ALCcontext_IncRef(ALCcontext *context)
-{
-    uint ref;
-    ref = IncrementRef(&context->ref);
-    TRACEREF("%p increasing refcount to %u\n", context, ref);
-}
-
-void ALCcontext_DecRef(ALCcontext *context)
-{
-    uint ref;
-    ref = DecrementRef(&context->ref);
-    TRACEREF("%p decreasing refcount to %u\n", context, ref);
-    if(ref == 0) FreeContext(context);
-}
-
-static void ReleaseThreadCtx(void *ptr)
-{
-    WARN("%p current for thread being destroyed\n", ptr);
-    ALCcontext_DecRef(ptr);
-}
-
-/* VerifyContext
- *
- * Checks that the given context is valid, and increments its reference count.
- */
-static ALCcontext *VerifyContext(ALCcontext *context)
-{
-    ALCdevice *dev;
-
-    LockLists();
-    dev = ATOMIC_LOAD(&DeviceList);
-    while(dev)
-    {
-        ALCcontext *ctx = ATOMIC_LOAD(&dev->ContextList);
-        while(ctx)
-        {
-            if(ctx == context)
-            {
-                ALCcontext_IncRef(ctx);
-                UnlockLists();
-                return ctx;
-            }
-            ctx = ctx->next;
-        }
-        dev = dev->next;
-    }
-    UnlockLists();
-
-    return NULL;
-}
-
-
-/* GetContextRef
- *
- * Returns the currently active context for this thread, and adds a reference
- * without locking it.
- */
-ALCcontext *GetContextRef(void)
-{
-    ALCcontext *context;
-
-    context = altss_get(LocalContext);
-    if(context)
-        ALCcontext_IncRef(context);
-    else
-    {
-        LockLists();
-        context = ATOMIC_LOAD(&GlobalContext);
-        if(context)
-            ALCcontext_IncRef(context);
-        UnlockLists();
-    }
-
-    return context;
-}
-
-
-/************************************************
- * Standard ALC functions
- ************************************************/
-
-/* alcGetError
- *
- * Return last ALC generated error code for the given device
-*/
-ALC_API ALCenum ALC_APIENTRY alcGetError(ALCdevice *device)
-{
-    ALCenum errorCode;
-
-    if(VerifyDevice(device))
-    {
-        errorCode = ATOMIC_EXCHANGE(ALCenum, &device->LastError, ALC_NO_ERROR);
-        ALCdevice_DecRef(device);
-    }
-    else
-        errorCode = ATOMIC_EXCHANGE(ALCenum, &LastNullDeviceError, ALC_NO_ERROR);
-
-    return errorCode;
-}
-
-
-/* alcSuspendContext
- *
- * Not functional
- */
-ALC_API ALCvoid ALC_APIENTRY alcSuspendContext(ALCcontext *context)
-{
-	if (!SuspendAndProcessSupported) return;
-
-	ALCdevice *Device;
-
-	LockLists();
-	/* alcGetContextsDevice sets an error for invalid contexts */
-	Device = alcGetContextsDevice(context);
-	if (Device && (Device->Flags & DEVICE_RUNNING))
-	{
-		V0(Device->Backend, stop)();
-		Device->Flags &= ~DEVICE_RUNNING;
-	}
-	UnlockLists();
-}
-
-/* alcProcessContext
- *
- * Not functional
- */
-ALC_API ALCvoid ALC_APIENTRY alcProcessContext(ALCcontext *context)
-{
-	if (!SuspendAndProcessSupported) return;
-
-	ALCdevice *Device;
-
-	LockLists();
-	/* alcGetContextsDevice sets an error for invalid contexts */
-	Device = alcGetContextsDevice(context);
-	if (Device && !(Device->Flags & DEVICE_RUNNING))
-	{
-		V0(Device->Backend, start)();
-		Device->Flags |= DEVICE_RUNNING;
-	}
-	UnlockLists();
-}
-
-
-/* alcGetString
- *
- * Returns information about the device, and error strings
- */
-ALC_API const ALCchar* ALC_APIENTRY alcGetString(ALCdevice *Device, ALCenum param)
-{
-    const ALCchar *value = NULL;
-
-    switch(param)
-    {
-    case ALC_NO_ERROR:
-        value = alcNoError;
-        break;
-
-    case ALC_INVALID_ENUM:
-        value = alcErrInvalidEnum;
-        break;
-
-    case ALC_INVALID_VALUE:
-        value = alcErrInvalidValue;
-        break;
-
-    case ALC_INVALID_DEVICE:
-        value = alcErrInvalidDevice;
-        break;
-
-    case ALC_INVALID_CONTEXT:
-        value = alcErrInvalidContext;
-        break;
-
-    case ALC_OUT_OF_MEMORY:
-        value = alcErrOutOfMemory;
-        break;
-
-    case ALC_DEVICE_SPECIFIER:
-        value = alcDefaultName;
-        break;
-
-    case ALC_ALL_DEVICES_SPECIFIER:
-        if(VerifyDevice(Device))
-        {
-            value = al_string_get_cstr(Device->DeviceName);
-            ALCdevice_DecRef(Device);
-        }
-        else
-        {
-            ProbeAllDevicesList();
-            value = al_string_get_cstr(alcAllDevicesList);
-        }
-        break;
-
-    case ALC_CAPTURE_DEVICE_SPECIFIER:
-        if(VerifyDevice(Device))
-        {
-            value = al_string_get_cstr(Device->DeviceName);
-            ALCdevice_DecRef(Device);
-        }
-        else
-        {
-            ProbeCaptureDeviceList();
-            value = al_string_get_cstr(alcCaptureDeviceList);
-        }
-        break;
-
-    /* Default devices are always first in the list */
-    case ALC_DEFAULT_DEVICE_SPECIFIER:
-        value = alcDefaultName;
-        break;
-
-    case ALC_DEFAULT_ALL_DEVICES_SPECIFIER:
-        if(al_string_empty(alcAllDevicesList))
-            ProbeAllDevicesList();
-
-        Device = VerifyDevice(Device);
-
-        free(alcDefaultAllDevicesSpecifier);
-        alcDefaultAllDevicesSpecifier = strdup(al_string_get_cstr(alcAllDevicesList));
-        if(!alcDefaultAllDevicesSpecifier)
-            alcSetError(Device, ALC_OUT_OF_MEMORY);
-
-        value = alcDefaultAllDevicesSpecifier;
-        if(Device) ALCdevice_DecRef(Device);
-        break;
-
-    case ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER:
-        if(al_string_empty(alcCaptureDeviceList))
-            ProbeCaptureDeviceList();
-
-        Device = VerifyDevice(Device);
-
-        free(alcCaptureDefaultDeviceSpecifier);
-        alcCaptureDefaultDeviceSpecifier = strdup(al_string_get_cstr(alcCaptureDeviceList));
-        if(!alcCaptureDefaultDeviceSpecifier)
-            alcSetError(Device, ALC_OUT_OF_MEMORY);
-
-        value = alcCaptureDefaultDeviceSpecifier;
-        if(Device) ALCdevice_DecRef(Device);
-        break;
-
-    case ALC_EXTENSIONS:
-        if(!VerifyDevice(Device))
-            value = alcNoDeviceExtList;
-        else
-        {
-            value = alcExtensionList;
-            ALCdevice_DecRef(Device);
-        }
-        break;
-
-    default:
-        Device = VerifyDevice(Device);
-        alcSetError(Device, ALC_INVALID_ENUM);
-        if(Device) ALCdevice_DecRef(Device);
-        break;
-    }
-
-    return value;
-}
-
-
-static ALCsizei GetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
-{
-    ALCsizei i;
-
-    if(size <= 0 || values == NULL)
-    {
-        alcSetError(device, ALC_INVALID_VALUE);
-        return 0;
-    }
-
-    if(!device)
-    {
-        switch(param)
-        {
-            case ALC_MAJOR_VERSION:
-                values[0] = alcMajorVersion;
-                return 1;
-            case ALC_MINOR_VERSION:
-                values[0] = alcMinorVersion;
-                return 1;
-
-            case ALC_ATTRIBUTES_SIZE:
-            case ALC_ALL_ATTRIBUTES:
-            case ALC_FREQUENCY:
-            case ALC_REFRESH:
-            case ALC_SYNC:
-            case ALC_MONO_SOURCES:
-            case ALC_STEREO_SOURCES:
-            case ALC_CAPTURE_SAMPLES:
-            case ALC_FORMAT_CHANNELS_SOFT:
-            case ALC_FORMAT_TYPE_SOFT:
-                alcSetError(NULL, ALC_INVALID_DEVICE);
-                return 0;
-
-            default:
-                alcSetError(NULL, ALC_INVALID_ENUM);
-                return 0;
-        }
-        return 0;
-    }
-
-    if(device->Type == Capture)
-    {
-        switch(param)
-        {
-            case ALC_CAPTURE_SAMPLES:
-                ALCdevice_Lock(device);
-                values[0] = V0(device->Backend,availableSamples)();
-                ALCdevice_Unlock(device);
-                return 1;
-
-            case ALC_CONNECTED:
-                values[0] = device->Connected;
-                return 1;
-
-            default:
-                alcSetError(device, ALC_INVALID_ENUM);
-                return 0;
-        }
-        return 0;
-    }
-
-    /* render device */
-    switch(param)
-    {
-        case ALC_MAJOR_VERSION:
-            values[0] = alcMajorVersion;
-            return 1;
-
-        case ALC_MINOR_VERSION:
-            values[0] = alcMinorVersion;
-            return 1;
-
-        case ALC_EFX_MAJOR_VERSION:
-            values[0] = alcEFXMajorVersion;
-            return 1;
-
-        case ALC_EFX_MINOR_VERSION:
-            values[0] = alcEFXMinorVersion;
-            return 1;
-
-        case ALC_ATTRIBUTES_SIZE:
-            values[0] = 15;
-            return 1;
-
-        case ALC_ALL_ATTRIBUTES:
-            if(size < 15)
-            {
-                alcSetError(device, ALC_INVALID_VALUE);
-                return 0;
-            }
-
-            i = 0;
-            values[i++] = ALC_FREQUENCY;
-            values[i++] = device->Frequency;
-
-            if(device->Type != Loopback)
-            {
-                values[i++] = ALC_REFRESH;
-                values[i++] = device->Frequency / device->UpdateSize;
-
-                values[i++] = ALC_SYNC;
-                values[i++] = ALC_FALSE;
-            }
-            else
-            {
-                values[i++] = ALC_FORMAT_CHANNELS_SOFT;
-                values[i++] = device->FmtChans;
-
-                values[i++] = ALC_FORMAT_TYPE_SOFT;
-                values[i++] = device->FmtType;
-            }
-
-            values[i++] = ALC_MONO_SOURCES;
-            values[i++] = device->NumMonoSources;
-
-            values[i++] = ALC_STEREO_SOURCES;
-            values[i++] = device->NumStereoSources;
-
-            values[i++] = ALC_MAX_AUXILIARY_SENDS;
-            values[i++] = device->NumAuxSends;
-
-            values[i++] = ALC_HRTF_SOFT;
-            values[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
-
-            values[i++] = 0;
-            return i;
-
-        case ALC_FREQUENCY:
-            values[0] = device->Frequency;
-            return 1;
-
-        case ALC_REFRESH:
-            if(device->Type == Loopback)
-            {
-                alcSetError(device, ALC_INVALID_DEVICE);
-                return 0;
-            }
-            values[0] = device->Frequency / device->UpdateSize;
-            return 1;
-
-        case ALC_SYNC:
-            if(device->Type == Loopback)
-            {
-                alcSetError(device, ALC_INVALID_DEVICE);
-                return 0;
-            }
-            values[0] = ALC_FALSE;
-            return 1;
-
-        case ALC_FORMAT_CHANNELS_SOFT:
-            if(device->Type != Loopback)
-            {
-                alcSetError(device, ALC_INVALID_DEVICE);
-                return 0;
-            }
-            values[0] = device->FmtChans;
-            return 1;
-
-        case ALC_FORMAT_TYPE_SOFT:
-            if(device->Type != Loopback)
-            {
-                alcSetError(device, ALC_INVALID_DEVICE);
-                return 0;
-            }
-            values[0] = device->FmtType;
-            return 1;
-
-        case ALC_MONO_SOURCES:
-            values[0] = device->NumMonoSources;
-            return 1;
-
-        case ALC_STEREO_SOURCES:
-            values[0] = device->NumStereoSources;
-            return 1;
-
-        case ALC_MAX_AUXILIARY_SENDS:
-            values[0] = device->NumAuxSends;
-            return 1;
-
-        case ALC_CONNECTED:
-            values[0] = device->Connected;
-            return 1;
-
-        case ALC_HRTF_SOFT:
-            values[0] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
-            return 1;
-
-        default:
-            alcSetError(device, ALC_INVALID_ENUM);
-            return 0;
-    }
-    return 0;
-}
-
-/* alcGetIntegerv
- *
- * Returns information about the device and the version of OpenAL
- */
-ALC_API void ALC_APIENTRY alcGetIntegerv(ALCdevice *device, ALCenum param, ALCsizei size, ALCint *values)
-{
-    device = VerifyDevice(device);
-    if(size <= 0 || values == NULL)
-        alcSetError(device, ALC_INVALID_VALUE);
-    else
-        GetIntegerv(device, param, size, values);
-    if(device) ALCdevice_DecRef(device);
-}
-
-ALC_API void ALC_APIENTRY alcGetInteger64vSOFT(ALCdevice *device, ALCenum pname, ALCsizei size, ALCint64SOFT *values)
-{
-    ALCint *ivals;
-    ALsizei i;
-
-    device = VerifyDevice(device);
-    if(size <= 0 || values == NULL)
-        alcSetError(device, ALC_INVALID_VALUE);
-    else if(!device || device->Type == Capture)
-    {
-        ivals = malloc(size * sizeof(ALCint));
-        size = GetIntegerv(device, pname, size, ivals);
-        for(i = 0;i < size;i++)
-            values[i] = ivals[i];
-        free(ivals);
-    }
-    else /* render device */
-    {
-        switch(pname)
-        {
-            case ALC_ATTRIBUTES_SIZE:
-                *values = 17;
-                break;
-
-            case ALC_ALL_ATTRIBUTES:
-                if(size < 17)
-                    alcSetError(device, ALC_INVALID_VALUE);
-                else
-                {
-                    int i = 0;
-
-                    V0(device->Backend,lock)();
-                    values[i++] = ALC_FREQUENCY;
-                    values[i++] = device->Frequency;
-
-                    if(device->Type != Loopback)
-                    {
-                        values[i++] = ALC_REFRESH;
-                        values[i++] = device->Frequency / device->UpdateSize;
-
-                        values[i++] = ALC_SYNC;
-                        values[i++] = ALC_FALSE;
-                    }
-                    else
-                    {
-                        values[i++] = ALC_FORMAT_CHANNELS_SOFT;
-                        values[i++] = device->FmtChans;
-
-                        values[i++] = ALC_FORMAT_TYPE_SOFT;
-                        values[i++] = device->FmtType;
-                    }
-
-                    values[i++] = ALC_MONO_SOURCES;
-                    values[i++] = device->NumMonoSources;
-
-                    values[i++] = ALC_STEREO_SOURCES;
-                    values[i++] = device->NumStereoSources;
-
-                    values[i++] = ALC_MAX_AUXILIARY_SENDS;
-                    values[i++] = device->NumAuxSends;
-
-                    values[i++] = ALC_HRTF_SOFT;
-                    values[i++] = (device->Hrtf ? ALC_TRUE : ALC_FALSE);
-
-                    values[i++] = ALC_DEVICE_CLOCK_SOFT;
-                    values[i++] = device->ClockBase +
-                                  (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency);
-
-                    values[i++] = 0;
-                    V0(device->Backend,unlock)();
-                }
-                break;
-
-            case ALC_DEVICE_CLOCK_SOFT:
-                V0(device->Backend,lock)();
-                *values = device->ClockBase +
-                          (device->SamplesDone * DEVICE_CLOCK_RES / device->Frequency);
-                V0(device->Backend,unlock)();
-                break;
-
-            default:
-                ivals = malloc(size * sizeof(ALCint));
-                size = GetIntegerv(device, pname, size, ivals);
-                for(i = 0;i < size;i++)
-                    values[i] = ivals[i];
-                free(ivals);
-                break;
-        }
-    }
-    if(device)
-        ALCdevice_DecRef(device);
-}
-
-
-/* alcIsExtensionPresent
- *
- * Determines if there is support for a particular extension
- */
-ALC_API ALCboolean ALC_APIENTRY alcIsExtensionPresent(ALCdevice *device, const ALCchar *extName)
-{
-    ALCboolean bResult = ALC_FALSE;
-
-    device = VerifyDevice(device);
-
-    if(!extName)
-        alcSetError(device, ALC_INVALID_VALUE);
-    else
-    {
-        size_t len = strlen(extName);
-        const char *ptr = (device ? alcExtensionList : alcNoDeviceExtList);
-        while(ptr && *ptr)
-        {
-            if(strncasecmp(ptr, extName, len) == 0 &&
-               (ptr[len] == '\0' || isspace(ptr[len])))
-            {
-                bResult = ALC_TRUE;
-                break;
-            }
-            if((ptr=strchr(ptr, ' ')) != NULL)
-            {
-                do {
-                    ++ptr;
-                } while(isspace(*ptr));
-            }
-        }
-    }
-    if(device)
-        ALCdevice_DecRef(device);
-    return bResult;
-}
-
-
-/* alcGetProcAddress
- *
- * Retrieves the function address for a particular extension function
- */
-ALC_API ALCvoid* ALC_APIENTRY alcGetProcAddress(ALCdevice *device, const ALCchar *funcName)
-{
-    ALCvoid *ptr = NULL;
-
-    if(!funcName)
-    {
-        device = VerifyDevice(device);
-        alcSetError(device, ALC_INVALID_VALUE);
-        if(device) ALCdevice_DecRef(device);
-    }
-    else
-    {
-        ALsizei i = 0;
-        while(alcFunctions[i].funcName && strcmp(alcFunctions[i].funcName, funcName) != 0)
-            i++;
-        ptr = alcFunctions[i].address;
-    }
-
-    return ptr;
-}
-
-
-/* alcGetEnumValue
- *
- * Get the value for a particular ALC enumeration name
- */
-ALC_API ALCenum ALC_APIENTRY alcGetEnumValue(ALCdevice *device, const ALCchar *enumName)
-{
-    ALCenum val = 0;
-
-    if(!enumName)
-    {
-        device = VerifyDevice(device);
-        alcSetError(device, ALC_INVALID_VALUE);
-        if(device) ALCdevice_DecRef(device);
-    }
-    else
-    {
-        ALsizei i = 0;
-        while(enumeration[i].enumName && strcmp(enumeration[i].enumName, enumName) != 0)
-            i++;
-        val = enumeration[i].value;
-    }
-
-    return val;
-}
-
-
-/* alcCreateContext
- *
- * Create and attach a context to the given device.
- */
-ALC_API ALCcontext* ALC_APIENTRY alcCreateContext(ALCdevice *device, const ALCint *attrList)
-{
-    ALCcontext *ALContext;
-    ALCenum err;
-
-    LockLists();
-    if(!(device=VerifyDevice(device)) || device->Type == Capture || !device->Connected)
-    {
-        UnlockLists();
-        alcSetError(device, ALC_INVALID_DEVICE);
-        if(device) ALCdevice_DecRef(device);
-        return NULL;
-    }
-
-    ATOMIC_STORE(&device->LastError, ALC_NO_ERROR);
-
-    if((err=UpdateDeviceParams(device, attrList)) != ALC_NO_ERROR)
-    {
-        UnlockLists();
-        alcSetError(device, err);
-        if(err == ALC_INVALID_DEVICE)
-        {
-            ALCdevice_Lock(device);
-            aluHandleDisconnect(device);
-            ALCdevice_Unlock(device);
-        }
-        ALCdevice_DecRef(device);
-        return NULL;
-    }
-
-    ALContext = al_calloc(16, sizeof(ALCcontext)+sizeof(ALlistener));
-    if(ALContext)
-    {
-        InitRef(&ALContext->ref, 1);
-        ALContext->Listener = (ALlistener*)ALContext->_listener_mem;
-
-        VECTOR_INIT(ALContext->ActiveAuxSlots);
-
-        ALContext->VoiceCount = 0;
-        ALContext->MaxVoices = 256;
-        ALContext->Voices = al_calloc(16, ALContext->MaxVoices * sizeof(ALContext->Voices[0]));
-    }
-    if(!ALContext || !ALContext->Voices)
-    {
-        if(!ATOMIC_LOAD(&device->ContextList))
-        {
-            V0(device->Backend,stop)();
-            device->Flags &= ~DEVICE_RUNNING;
-        }
-        UnlockLists();
-
-        if(ALContext)
-        {
-            al_free(ALContext->Voices);
-            ALContext->Voices = NULL;
-
-            VECTOR_DEINIT(ALContext->ActiveAuxSlots);
-
-            al_free(ALContext);
-            ALContext = NULL;
-        }
-
-        alcSetError(device, ALC_OUT_OF_MEMORY);
-        ALCdevice_DecRef(device);
-        return NULL;
-    }
-
-    ALContext->Device = device;
-    ALCdevice_IncRef(device);
-    InitContext(ALContext);
-
-    {
-        ALCcontext *head = ATOMIC_LOAD(&device->ContextList);
-        do {
-            ALContext->next = head;
-        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCcontext*, &device->ContextList, &head, ALContext));
-    }
-    UnlockLists();
-
-    ALCdevice_DecRef(device);
-
-    TRACE("Created context %p\n", ALContext);
-    return ALContext;
-}
-
-/* alcDestroyContext
- *
- * Remove a context from its device
- */
-ALC_API ALCvoid ALC_APIENTRY alcDestroyContext(ALCcontext *context)
-{
-    ALCdevice *Device;
-
-    LockLists();
-    /* alcGetContextsDevice sets an error for invalid contexts */
-    Device = alcGetContextsDevice(context);
-    if(Device)
-    {
-        ReleaseContext(context, Device);
-        if(!ATOMIC_LOAD(&Device->ContextList))
-        {
-            V0(Device->Backend,stop)();
-            Device->Flags &= ~DEVICE_RUNNING;
-        }
-    }
-    UnlockLists();
-}
-
-
-/* alcGetCurrentContext
- *
- * Returns the currently active context on the calling thread
- */
-ALC_API ALCcontext* ALC_APIENTRY alcGetCurrentContext(void)
-{
-    ALCcontext *Context = altss_get(LocalContext);
-    if(!Context) Context = ATOMIC_LOAD(&GlobalContext);
-    return Context;
-}
-
-/* alcGetThreadContext
- *
- * Returns the currently active thread-local context
- */
-ALC_API ALCcontext* ALC_APIENTRY alcGetThreadContext(void)
-{
-    return altss_get(LocalContext);
-}
-
-
-/* alcMakeContextCurrent
- *
- * Makes the given context the active process-wide context, and removes the
- * thread-local context for the calling thread.
- */
-ALC_API ALCboolean ALC_APIENTRY alcMakeContextCurrent(ALCcontext *context)
-{
-    /* context must be valid or NULL */
-    if(context && !(context=VerifyContext(context)))
-    {
-        alcSetError(NULL, ALC_INVALID_CONTEXT);
-        return ALC_FALSE;
-    }
-    /* context's reference count is already incremented */
-    context = ATOMIC_EXCHANGE(ALCcontext*, &GlobalContext, context);
-    if(context) ALCcontext_DecRef(context);
-
-    if((context=altss_get(LocalContext)) != NULL)
-    {
-        altss_set(LocalContext, NULL);
-        ALCcontext_DecRef(context);
-    }
-
-    return ALC_TRUE;
-}
-
-/* alcSetThreadContext
- *
- * Makes the given context the active context for the current thread
- */
-ALC_API ALCboolean ALC_APIENTRY alcSetThreadContext(ALCcontext *context)
-{
-    ALCcontext *old;
-
-    /* context must be valid or NULL */
-    if(context && !(context=VerifyContext(context)))
-    {
-        alcSetError(NULL, ALC_INVALID_CONTEXT);
-        return ALC_FALSE;
-    }
-    /* context's reference count is already incremented */
-    old = altss_get(LocalContext);
-    altss_set(LocalContext, context);
-    if(old) ALCcontext_DecRef(old);
-
-    return ALC_TRUE;
-}
-
-
-/* alcGetContextsDevice
- *
- * Returns the device that a particular context is attached to
- */
-ALC_API ALCdevice* ALC_APIENTRY alcGetContextsDevice(ALCcontext *Context)
-{
-    ALCdevice *Device;
-
-    if(!(Context=VerifyContext(Context)))
-    {
-        alcSetError(NULL, ALC_INVALID_CONTEXT);
-        return NULL;
-    }
-    Device = Context->Device;
-    ALCcontext_DecRef(Context);
-
-    return Device;
-}
-
-
-/* alcOpenDevice
- *
- * Opens the named device.
- */
-ALC_API ALCdevice* ALC_APIENTRY alcOpenDevice(const ALCchar *deviceName)
-{
-    const ALCchar *fmt;
-    ALCdevice *device;
-    ALCenum err;
-
-    DO_INITCONFIG();
-
-    if(!PlaybackBackend.name)
-    {
-        alcSetError(NULL, ALC_INVALID_VALUE);
-        return NULL;
-    }
-	SuspendAndProcessSupported = strcasecmp(PlaybackBackend.name, "mmdevapi") == 0;
-
-    if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0))
-        deviceName = NULL;
-
-    device = al_calloc(16, sizeof(ALCdevice)+sizeof(ALeffectslot));
-    if(!device)
-    {
-        alcSetError(NULL, ALC_OUT_OF_MEMORY);
-        return NULL;
-    }
-
-    //Validate device
-    InitRef(&device->ref, 1);
-    device->Connected = ALC_TRUE;
-    device->Type = Playback;
-    ATOMIC_INIT(&device->LastError, ALC_NO_ERROR);
-
-    device->Flags = 0;
-    device->Bs2b = NULL;
-    device->Bs2bLevel = 0;
-    AL_STRING_INIT(device->DeviceName);
-
-    ATOMIC_INIT(&device->ContextList, NULL);
-
-    device->ClockBase = 0;
-    device->SamplesDone = 0;
-
-    device->MaxNoOfSources = 256;
-    device->AuxiliaryEffectSlotMax = 4;
-    device->NumAuxSends = MAX_SENDS;
-
-    InitUIntMap(&device->BufferMap, ~0);
-    InitUIntMap(&device->EffectMap, ~0);
-    InitUIntMap(&device->FilterMap, ~0);
-    InitUIntMap(&device->SfontMap, ~0);
-    InitUIntMap(&device->PresetMap, ~0);
-    InitUIntMap(&device->FontsoundMap, ~0);
-
-    //Set output format
-    device->FmtChans = DevFmtChannelsDefault;
-    device->FmtType = DevFmtTypeDefault;
-    device->Frequency = DEFAULT_OUTPUT_RATE;
-    device->NumUpdates = 4;
-    device->UpdateSize = 1024;
-
-    if(!PlaybackBackend.getFactory)
-        device->Backend = create_backend_wrapper(device, &PlaybackBackend.Funcs,
-                                                 ALCbackend_Playback);
-    else
-    {
-        ALCbackendFactory *factory = PlaybackBackend.getFactory();
-        device->Backend = V(factory,createBackend)(device, ALCbackend_Playback);
-    }
-    if(!device->Backend)
-    {
-        al_free(device);
-        alcSetError(NULL, ALC_OUT_OF_MEMORY);
-        return NULL;
-    }
-
-
-    if(ConfigValueStr(NULL, "channels", &fmt))
-    {
-        static const struct {
-            const char name[16];
-            enum DevFmtChannels chans;
-        } chanlist[] = {
-            { "mono",       DevFmtMono   },
-            { "stereo",     DevFmtStereo },
-            { "quad",       DevFmtQuad   },
-            { "surround51", DevFmtX51    },
-            { "surround61", DevFmtX61    },
-            { "surround71", DevFmtX71    },
-        };
-        size_t i;
-
-        for(i = 0;i < COUNTOF(chanlist);i++)
-        {
-            if(strcasecmp(chanlist[i].name, fmt) == 0)
-            {
-                device->FmtChans = chanlist[i].chans;
-                device->Flags |= DEVICE_CHANNELS_REQUEST;
-                break;
-            }
-        }
-        if(i == COUNTOF(chanlist))
-            ERR("Unsupported channels: %s\n", fmt);
-    }
-    if(ConfigValueStr(NULL, "sample-type", &fmt))
-    {
-        static const struct {
-            const char name[16];
-            enum DevFmtType type;
-        } typelist[] = {
-            { "int8",    DevFmtByte   },
-            { "uint8",   DevFmtUByte  },
-            { "int16",   DevFmtShort  },
-            { "uint16",  DevFmtUShort },
-            { "int32",   DevFmtInt    },
-            { "uint32",  DevFmtUInt   },
-            { "float32", DevFmtFloat  },
-        };
-        size_t i;
-
-        for(i = 0;i < COUNTOF(typelist);i++)
-        {
-            if(strcasecmp(typelist[i].name, fmt) == 0)
-            {
-                device->FmtType = typelist[i].type;
-                device->Flags |= DEVICE_SAMPLE_TYPE_REQUEST;
-                break;
-            }
-        }
-        if(i == COUNTOF(typelist))
-            ERR("Unsupported sample-type: %s\n", fmt);
-    }
-#define DEVICE_FORMAT_REQUEST (DEVICE_CHANNELS_REQUEST|DEVICE_SAMPLE_TYPE_REQUEST)
-    if((device->Flags&DEVICE_FORMAT_REQUEST) != DEVICE_FORMAT_REQUEST &&
-       ConfigValueStr(NULL, "format", &fmt))
-    {
-        static const struct {
-            const char name[32];
-            enum DevFmtChannels channels;
-            enum DevFmtType type;
-        } formats[] = {
-            { "AL_FORMAT_MONO32",   DevFmtMono,   DevFmtFloat },
-            { "AL_FORMAT_STEREO32", DevFmtStereo, DevFmtFloat },
-            { "AL_FORMAT_QUAD32",   DevFmtQuad,   DevFmtFloat },
-            { "AL_FORMAT_51CHN32",  DevFmtX51,    DevFmtFloat },
-            { "AL_FORMAT_61CHN32",  DevFmtX61,    DevFmtFloat },
-            { "AL_FORMAT_71CHN32",  DevFmtX71,    DevFmtFloat },
-
-            { "AL_FORMAT_MONO16",   DevFmtMono,   DevFmtShort },
-            { "AL_FORMAT_STEREO16", DevFmtStereo, DevFmtShort },
-            { "AL_FORMAT_QUAD16",   DevFmtQuad,   DevFmtShort },
-            { "AL_FORMAT_51CHN16",  DevFmtX51,    DevFmtShort },
-            { "AL_FORMAT_61CHN16",  DevFmtX61,    DevFmtShort },
-            { "AL_FORMAT_71CHN16",  DevFmtX71,    DevFmtShort },
-
-            { "AL_FORMAT_MONO8",   DevFmtMono,   DevFmtByte },
-            { "AL_FORMAT_STEREO8", DevFmtStereo, DevFmtByte },
-            { "AL_FORMAT_QUAD8",   DevFmtQuad,   DevFmtByte },
-            { "AL_FORMAT_51CHN8",  DevFmtX51,    DevFmtByte },
-            { "AL_FORMAT_61CHN8",  DevFmtX61,    DevFmtByte },
-            { "AL_FORMAT_71CHN8",  DevFmtX71,    DevFmtByte }
-        };
-        size_t i;
-
-        ERR("Option 'format' is deprecated, please use 'channels' and 'sample-type'\n");
-        for(i = 0;i < COUNTOF(formats);i++)
-        {
-            if(strcasecmp(fmt, formats[i].name) == 0)
-            {
-                if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
-                    device->FmtChans = formats[i].channels;
-                if(!(device->Flags&DEVICE_SAMPLE_TYPE_REQUEST))
-                    device->FmtType = formats[i].type;
-                device->Flags |= DEVICE_FORMAT_REQUEST;
-                break;
-            }
-        }
-        if(i == COUNTOF(formats))
-            ERR("Unsupported format: %s\n", fmt);
-    }
-#undef DEVICE_FORMAT_REQUEST
-
-    if(ConfigValueUInt(NULL, "frequency", &device->Frequency))
-    {
-        device->Flags |= DEVICE_FREQUENCY_REQUEST;
-        if(device->Frequency < MIN_OUTPUT_RATE)
-            ERR("%uhz request clamped to %uhz minimum\n", device->Frequency, MIN_OUTPUT_RATE);
-        device->Frequency = maxu(device->Frequency, MIN_OUTPUT_RATE);
-    }
-
-    ConfigValueUInt(NULL, "periods", &device->NumUpdates);
-    device->NumUpdates = clampu(device->NumUpdates, 2, 16);
-
-    ConfigValueUInt(NULL, "period_size", &device->UpdateSize);
-    device->UpdateSize = clampu(device->UpdateSize, 64, 8192);
-    if((CPUCapFlags&CPU_CAP_SSE))
-        device->UpdateSize = (device->UpdateSize+3)&~3;
-
-    ConfigValueUInt(NULL, "sources", &device->MaxNoOfSources);
-    if(device->MaxNoOfSources == 0) device->MaxNoOfSources = 256;
-
-    ConfigValueUInt(NULL, "slots", &device->AuxiliaryEffectSlotMax);
-    if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4;
-
-    ConfigValueUInt(NULL, "sends", &device->NumAuxSends);
-    if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS;
-
-    ConfigValueInt(NULL, "cf_level", &device->Bs2bLevel);
-
-    device->NumStereoSources = 1;
-    device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources;
-
-    device->Synth = SynthCreate(device);
-    if(!device->Synth)
-    {
-        DELETE_OBJ(device->Backend);
-        al_free(device);
-        alcSetError(NULL, ALC_OUT_OF_MEMORY);
-        return NULL;
-    }
-
-    // Find a playback device to open
-    if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
-    {
-        DELETE_OBJ(device->Synth);
-        DELETE_OBJ(device->Backend);
-        al_free(device);
-        alcSetError(NULL, err);
-        return NULL;
-    }
-
-    if(DefaultEffect.type != AL_EFFECT_NULL)
-    {
-        device->DefaultSlot = (ALeffectslot*)device->_slot_mem;
-        if(InitEffectSlot(device->DefaultSlot) != AL_NO_ERROR)
-        {
-            device->DefaultSlot = NULL;
-            ERR("Failed to initialize the default effect slot\n");
-        }
-        else if(InitializeEffect(device, device->DefaultSlot, &DefaultEffect) != AL_NO_ERROR)
-        {
-            ALeffectState *state = device->DefaultSlot->EffectState;
-            device->DefaultSlot = NULL;
-            DELETE_OBJ(state);
-            ERR("Failed to initialize the default effect\n");
-        }
-    }
-
-    {
-        ALCdevice *head = ATOMIC_LOAD(&DeviceList);
-        do {
-            device->next = head;
-        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
-    }
-
-    TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName));
-    return device;
-}
-
-/* alcCloseDevice
- *
- * Closes the given device.
- */
-ALC_API ALCboolean ALC_APIENTRY alcCloseDevice(ALCdevice *device)
-{
-    ALCdevice *list, *origdev, *nextdev;
-    ALCcontext *ctx;
-
-    LockLists();
-    list = ATOMIC_LOAD(&DeviceList);
-    do {
-        if(list == device)
-            break;
-    } while((list=list->next) != NULL);
-    if(!list || list->Type == Capture)
-    {
-        alcSetError(list, ALC_INVALID_DEVICE);
-        UnlockLists();
-        return ALC_FALSE;
-    }
-
-    origdev = device;
-    nextdev = device->next;
-    if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &origdev, nextdev))
-    {
-        do {
-            list = origdev;
-            origdev = device;
-        } while(!COMPARE_EXCHANGE(&list->next, &origdev, nextdev));
-    }
-    UnlockLists();
-
-    ctx = ATOMIC_LOAD(&device->ContextList);
-    while(ctx != NULL)
-    {
-        ALCcontext *next = ctx->next;
-        WARN("Releasing context %p\n", ctx);
-        ReleaseContext(ctx, device);
-        ctx = next;
-    }
-    if((device->Flags&DEVICE_RUNNING))
-        V0(device->Backend,stop)();
-    device->Flags &= ~DEVICE_RUNNING;
-
-    ALCdevice_DecRef(device);
-
-    return ALC_TRUE;
-}
-
-
-/************************************************
- * ALC capture functions
- ************************************************/
-ALC_API ALCdevice* ALC_APIENTRY alcCaptureOpenDevice(const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei samples)
-{
-    ALCdevice *device = NULL;
-    ALCenum err;
-
-    DO_INITCONFIG();
-
-    if(!CaptureBackend.name)
-    {
-        alcSetError(NULL, ALC_INVALID_VALUE);
-        return NULL;
-    }
-
-    if(samples <= 0)
-    {
-        alcSetError(NULL, ALC_INVALID_VALUE);
-        return NULL;
-    }
-
-    if(deviceName && (!deviceName[0] || strcasecmp(deviceName, alcDefaultName) == 0 || strcasecmp(deviceName, "openal-soft") == 0))
-        deviceName = NULL;
-
-    device = al_calloc(16, sizeof(ALCdevice));
-    if(!device)
-    {
-        alcSetError(NULL, ALC_OUT_OF_MEMORY);
-        return NULL;
-    }
-
-    //Validate device
-    InitRef(&device->ref, 1);
-    device->Connected = ALC_TRUE;
-    device->Type = Capture;
-
-    AL_STRING_INIT(device->DeviceName);
-
-    InitUIntMap(&device->BufferMap, ~0);
-    InitUIntMap(&device->EffectMap, ~0);
-    InitUIntMap(&device->FilterMap, ~0);
-    InitUIntMap(&device->SfontMap, ~0);
-    InitUIntMap(&device->PresetMap, ~0);
-    InitUIntMap(&device->FontsoundMap, ~0);
-
-    if(!CaptureBackend.getFactory)
-        device->Backend = create_backend_wrapper(device, &CaptureBackend.Funcs,
-                                                 ALCbackend_Capture);
-    else
-    {
-        ALCbackendFactory *factory = CaptureBackend.getFactory();
-        device->Backend = V(factory,createBackend)(device, ALCbackend_Capture);
-    }
-    if(!device->Backend)
-    {
-        al_free(device);
-        alcSetError(NULL, ALC_OUT_OF_MEMORY);
-        return NULL;
-    }
-
-    device->Flags |= DEVICE_FREQUENCY_REQUEST;
-    device->Frequency = frequency;
-
-    device->Flags |= DEVICE_CHANNELS_REQUEST | DEVICE_SAMPLE_TYPE_REQUEST;
-    if(DecomposeDevFormat(format, &device->FmtChans, &device->FmtType) == AL_FALSE)
-    {
-        al_free(device);
-        alcSetError(NULL, ALC_INVALID_ENUM);
-        return NULL;
-    }
-
-    device->UpdateSize = samples;
-    device->NumUpdates = 1;
-
-    if((err=V(device->Backend,open)(deviceName)) != ALC_NO_ERROR)
-    {
-        al_free(device);
-        alcSetError(NULL, err);
-        return NULL;
-    }
-
-    {
-        ALCdevice *head = ATOMIC_LOAD(&DeviceList);
-        do {
-            device->next = head;
-        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
-    }
-
-    TRACE("Created device %p, \"%s\"\n", device, al_string_get_cstr(device->DeviceName));
-    return device;
-}
-
-ALC_API ALCboolean ALC_APIENTRY alcCaptureCloseDevice(ALCdevice *device)
-{
-    ALCdevice *list, *next, *nextdev;
-
-    LockLists();
-    list = ATOMIC_LOAD(&DeviceList);
-    do {
-        if(list == device)
-            break;
-    } while((list=list->next) != NULL);
-    if(!list || list->Type != Capture)
-    {
-        alcSetError(list, ALC_INVALID_DEVICE);
-        UnlockLists();
-        return ALC_FALSE;
-    }
-
-    next = device;
-    nextdev = device->next;
-    if(!ATOMIC_COMPARE_EXCHANGE_STRONG(ALCdevice*, &DeviceList, &next, nextdev))
-    {
-        do {
-            list = next;
-            next = device;
-        } while(!COMPARE_EXCHANGE(&list->next, &next, nextdev));
-    }
-    UnlockLists();
-
-    ALCdevice_DecRef(device);
-
-    return ALC_TRUE;
-}
-
-ALC_API void ALC_APIENTRY alcCaptureStart(ALCdevice *device)
-{
-    if(!(device=VerifyDevice(device)) || device->Type != Capture)
-        alcSetError(device, ALC_INVALID_DEVICE);
-    else
-    {
-        ALCdevice_Lock(device);
-        if(device->Connected)
-        {
-            if(!(device->Flags&DEVICE_RUNNING))
-                V0(device->Backend,start)();
-            device->Flags |= DEVICE_RUNNING;
-        }
-        ALCdevice_Unlock(device);
-    }
-
-    if(device) ALCdevice_DecRef(device);
-}
-
-ALC_API void ALC_APIENTRY alcCaptureStop(ALCdevice *device)
-{
-    if(!(device=VerifyDevice(device)) || device->Type != Capture)
-        alcSetError(device, ALC_INVALID_DEVICE);
-    else
-    {
-        ALCdevice_Lock(device);
-        if((device->Flags&DEVICE_RUNNING))
-            V0(device->Backend,stop)();
-        device->Flags &= ~DEVICE_RUNNING;
-        ALCdevice_Unlock(device);
-    }
-
-    if(device) ALCdevice_DecRef(device);
-}
-
-ALC_API void ALC_APIENTRY alcCaptureSamples(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
-{
-    if(!(device=VerifyDevice(device)) || device->Type != Capture)
-        alcSetError(device, ALC_INVALID_DEVICE);
-    else
-    {
-        ALCenum err = ALC_INVALID_VALUE;
-
-        ALCdevice_Lock(device);
-        if(samples >= 0 && V0(device->Backend,availableSamples)() >= (ALCuint)samples)
-            err = V(device->Backend,captureSamples)(buffer, samples);
-        ALCdevice_Unlock(device);
-
-        if(err != ALC_NO_ERROR)
-            alcSetError(device, err);
-    }
-    if(device) ALCdevice_DecRef(device);
-}
-
-
-/************************************************
- * ALC loopback functions
- ************************************************/
-
-/* alcLoopbackOpenDeviceSOFT
- *
- * Open a loopback device, for manual rendering.
- */
-ALC_API ALCdevice* ALC_APIENTRY alcLoopbackOpenDeviceSOFT(const ALCchar *deviceName)
-{
-    ALCbackendFactory *factory;
-    ALCdevice *device;
-
-    DO_INITCONFIG();
-
-    /* Make sure the device name, if specified, is us. */
-    if(deviceName && strcmp(deviceName, alcDefaultName) != 0)
-    {
-        alcSetError(NULL, ALC_INVALID_VALUE);
-        return NULL;
-    }
-
-    device = al_calloc(16, sizeof(ALCdevice));
-    if(!device)
-    {
-        alcSetError(NULL, ALC_OUT_OF_MEMORY);
-        return NULL;
-    }
-
-    //Validate device
-    InitRef(&device->ref, 1);
-    device->Connected = ALC_TRUE;
-    device->Type = Loopback;
-    ATOMIC_INIT(&device->LastError, ALC_NO_ERROR);
-
-    device->Flags = 0;
-    device->Bs2b = NULL;
-    device->Bs2bLevel = 0;
-    AL_STRING_INIT(device->DeviceName);
-
-    ATOMIC_INIT(&device->ContextList, NULL);
-
-    device->ClockBase = 0;
-    device->SamplesDone = 0;
-
-    device->MaxNoOfSources = 256;
-    device->AuxiliaryEffectSlotMax = 4;
-    device->NumAuxSends = MAX_SENDS;
-
-    InitUIntMap(&device->BufferMap, ~0);
-    InitUIntMap(&device->EffectMap, ~0);
-    InitUIntMap(&device->FilterMap, ~0);
-    InitUIntMap(&device->SfontMap, ~0);
-    InitUIntMap(&device->PresetMap, ~0);
-    InitUIntMap(&device->FontsoundMap, ~0);
-
-    factory = ALCloopbackFactory_getFactory();
-    device->Backend = V(factory,createBackend)(device, ALCbackend_Loopback);
-    if(!device->Backend)
-    {
-        al_free(device);
-        alcSetError(NULL, ALC_OUT_OF_MEMORY);
-        return NULL;
-    }
-
-    //Set output format
-    device->NumUpdates = 0;
-    device->UpdateSize = 0;
-
-    device->Frequency = DEFAULT_OUTPUT_RATE;
-    device->FmtChans = DevFmtChannelsDefault;
-    device->FmtType = DevFmtTypeDefault;
-
-    ConfigValueUInt(NULL, "sources", &device->MaxNoOfSources);
-    if(device->MaxNoOfSources == 0) device->MaxNoOfSources = 256;
-
-    ConfigValueUInt(NULL, "slots", &device->AuxiliaryEffectSlotMax);
-    if(device->AuxiliaryEffectSlotMax == 0) device->AuxiliaryEffectSlotMax = 4;
-
-    ConfigValueUInt(NULL, "sends", &device->NumAuxSends);
-    if(device->NumAuxSends > MAX_SENDS) device->NumAuxSends = MAX_SENDS;
-
-    device->NumStereoSources = 1;
-    device->NumMonoSources = device->MaxNoOfSources - device->NumStereoSources;
-
-    device->Synth = SynthCreate(device);
-    if(!device->Synth)
-    {
-        DELETE_OBJ(device->Backend);
-        al_free(device);
-        alcSetError(NULL, ALC_OUT_OF_MEMORY);
-        return NULL;
-    }
-
-    // Open the "backend"
-    V(device->Backend,open)("Loopback");
-
-    {
-        ALCdevice *head = ATOMIC_LOAD(&DeviceList);
-        do {
-            device->next = head;
-        } while(!ATOMIC_COMPARE_EXCHANGE_WEAK(ALCdevice*, &DeviceList, &head, device));
-    }
-
-    TRACE("Created device %p\n", device);
-    return device;
-}
-
-/* alcIsRenderFormatSupportedSOFT
- *
- * Determines if the loopback device supports the given format for rendering.
- */
-ALC_API ALCboolean ALC_APIENTRY alcIsRenderFormatSupportedSOFT(ALCdevice *device, ALCsizei freq, ALCenum channels, ALCenum type)
-{
-    ALCboolean ret = ALC_FALSE;
-
-    if(!(device=VerifyDevice(device)) || device->Type != Loopback)
-        alcSetError(device, ALC_INVALID_DEVICE);
-    else if(freq <= 0)
-        alcSetError(device, ALC_INVALID_VALUE);
-    else
-    {
-        if(IsValidALCType(type) && BytesFromDevFmt(type) > 0 &&
-           IsValidALCChannels(channels) && ChannelsFromDevFmt(channels) > 0 &&
-           freq >= MIN_OUTPUT_RATE)
-            ret = ALC_TRUE;
-    }
-    if(device) ALCdevice_DecRef(device);
-
-    return ret;
-}
-
-/* alcRenderSamplesSOFT
- *
- * Renders some samples into a buffer, using the format last set by the
- * attributes given to alcCreateContext.
- */
-FORCE_ALIGN ALC_API void ALC_APIENTRY alcRenderSamplesSOFT(ALCdevice *device, ALCvoid *buffer, ALCsizei samples)
-{
-    if(!(device=VerifyDevice(device)) || device->Type != Loopback)
-        alcSetError(device, ALC_INVALID_DEVICE);
-    else if(samples < 0 || (samples > 0 && buffer == NULL))
-        alcSetError(device, ALC_INVALID_VALUE);
-    else
-        aluMixData(device, buffer, samples);
-    if(device) ALCdevice_DecRef(device);
-}
-
-
-/************************************************
- * ALC DSP pause/resume functions
- ************************************************/
-
-/* alcDevicePauseSOFT
- *
- * Pause the DSP to stop audio processing.
- */
-ALC_API void ALC_APIENTRY alcDevicePauseSOFT(ALCdevice *device)
-{
-    if(!(device=VerifyDevice(device)) || device->Type != Playback)
-        alcSetError(device, ALC_INVALID_DEVICE);
-    else
-    {
-        LockLists();
-        if((device->Flags&DEVICE_RUNNING))
-            V0(device->Backend,stop)();
-        device->Flags &= ~DEVICE_RUNNING;
-        device->Flags |= DEVICE_PAUSED;
-        UnlockLists();
-    }
-    if(device) ALCdevice_DecRef(device);
-}
-
-/* alcDeviceResumeSOFT
- *
- * Resume the DSP to restart audio processing.
- */
-ALC_API void ALC_APIENTRY alcDeviceResumeSOFT(ALCdevice *device)
-{
-    if(!(device=VerifyDevice(device)) || device->Type != Playback)
-        alcSetError(device, ALC_INVALID_DEVICE);
-    else
-    {
-        LockLists();
-        if((device->Flags&DEVICE_PAUSED))
-        {
-            device->Flags &= ~DEVICE_PAUSED;
-            if(ATOMIC_LOAD(&device->ContextList) != NULL)
-            {
-                if(V0(device->Backend,start)() != ALC_FALSE)
-                    device->Flags |= DEVICE_RUNNING;
-                else
-                {
-                    alcSetError(device, ALC_INVALID_DEVICE);
-                    ALCdevice_Lock(device);
-                    aluHandleDisconnect(device);
-                    ALCdevice_Unlock(device);
-                }
-            }
-        }
-        UnlockLists();
-    }
-    if(device) ALCdevice_DecRef(device);
-}
diff --git a/Telegram/_openal_patch/Alc/backends/mmdevapi.c b/Telegram/_openal_patch/Alc/backends/mmdevapi.c
new file mode 100644
index 0000000000..8a6f9fbadf
--- /dev/null
+++ b/Telegram/_openal_patch/Alc/backends/mmdevapi.c
@@ -0,0 +1,1789 @@
+/**
+ * OpenAL cross platform audio library
+ * Copyright (C) 2011 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"
+
+#define COBJMACROS
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+
+#include <mmdeviceapi.h>
+#include <audioclient.h>
+#include <cguid.h>
+#include <devpropdef.h>
+#include <mmreg.h>
+#include <propsys.h>
+#include <propkey.h>
+#include <devpkey.h>
+#ifndef _WAVEFORMATEXTENSIBLE_
+#include <ks.h>
+#include <ksmedia.h>
+#endif
+
+#include "alMain.h"
+#include "alu.h"
+#include "threads.h"
+#include "compat.h"
+#include "alstring.h"
+
+#include "backends/base.h"
+
+
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
+
+DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
+DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
+
+#define MONO SPEAKER_FRONT_CENTER
+#define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
+#define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
+#define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
+#define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
+#define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
+#define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
+#define X7DOT1_WIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
+
+
+typedef struct {
+    al_string name;
+    WCHAR *devid;
+} DevMap;
+TYPEDEF_VECTOR(DevMap, vector_DevMap)
+
+static void clear_devlist(vector_DevMap *list)
+{
+#define CLEAR_DEVMAP(i) do {     \
+    AL_STRING_DEINIT((i)->name); \
+    free((i)->devid);            \
+    (i)->devid = NULL;           \
+} while(0)
+    VECTOR_FOR_EACH(DevMap, *list, CLEAR_DEVMAP);
+    VECTOR_RESIZE(*list, 0);
+#undef CLEAR_DEVMAP
+}
+
+static vector_DevMap PlaybackDevices;
+static vector_DevMap CaptureDevices;
+
+
+static HANDLE ThreadHdl;
+static DWORD ThreadID;
+
+typedef struct {
+    HANDLE FinishedEvt;
+    HRESULT result;
+} ThreadRequest;
+
+#define WM_USER_First       (WM_USER+0)
+#define WM_USER_OpenDevice  (WM_USER+0)
+#define WM_USER_ResetDevice (WM_USER+1)
+#define WM_USER_StartDevice (WM_USER+2)
+#define WM_USER_StopDevice  (WM_USER+3)
+#define WM_USER_CloseDevice (WM_USER+4)
+#define WM_USER_Enumerate   (WM_USER+5)
+#define WM_USER_Last        (WM_USER+5)
+
+static inline void ReturnMsgResponse(ThreadRequest *req, HRESULT res)
+{
+    req->result = res;
+    SetEvent(req->FinishedEvt);
+}
+
+static HRESULT WaitForResponse(ThreadRequest *req)
+{
+    if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
+        return req->result;
+    ERR("Message response error: %lu\n", GetLastError());
+    return E_FAIL;
+}
+
+
+static void get_device_name(IMMDevice *device, al_string *name)
+{
+    IPropertyStore *ps;
+    PROPVARIANT pvname;
+    HRESULT hr;
+
+    hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
+    if(FAILED(hr))
+    {
+        WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
+        return;
+    }
+
+    PropVariantInit(&pvname);
+
+    hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
+    if(FAILED(hr))
+        WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr);
+    else if(pvname.vt == VT_LPWSTR)
+        al_string_copy_wcstr(name, pvname.pwszVal);
+    else
+        WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname.vt);
+
+    PropVariantClear(&pvname);
+    IPropertyStore_Release(ps);
+}
+
+static void get_device_formfactor(IMMDevice *device, EndpointFormFactor *formfactor)
+{
+    IPropertyStore *ps;
+    PROPVARIANT pvform;
+    HRESULT hr;
+
+    hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
+    if(FAILED(hr))
+    {
+        WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
+        return;
+    }
+
+    PropVariantInit(&pvform);
+
+    hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_FormFactor, &pvform);
+    if(FAILED(hr))
+        WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr);
+    else if(pvform.vt == VT_UI4)
+        *formfactor = pvform.ulVal;
+    else if(pvform.vt == VT_EMPTY)
+        *formfactor = UnknownFormFactor;
+    else
+        WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform.vt);
+
+    PropVariantClear(&pvform);
+    IPropertyStore_Release(ps);
+}
+
+
+static void add_device(IMMDevice *device, LPCWSTR devid, vector_DevMap *list)
+{
+    DevMap entry;
+
+    AL_STRING_INIT(entry.name);
+    entry.devid = strdupW(devid);
+    get_device_name(device, &entry.name);
+
+    TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry.name), entry.devid);
+    VECTOR_PUSH_BACK(*list, entry);
+}
+
+static LPWSTR get_device_id(IMMDevice *device)
+{
+    LPWSTR devid;
+    HRESULT hr;
+
+    hr = IMMDevice_GetId(device, &devid);
+    if(FAILED(hr))
+    {
+        ERR("Failed to get device id: %lx\n", hr);
+        return NULL;
+    }
+
+    return devid;
+}
+
+static HRESULT probe_devices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, vector_DevMap *list)
+{
+    IMMDeviceCollection *coll;
+    IMMDevice *defdev = NULL;
+    LPWSTR defdevid = NULL;
+    HRESULT hr;
+    UINT count;
+    UINT i;
+
+    hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
+    if(FAILED(hr))
+    {
+        ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    count = 0;
+    hr = IMMDeviceCollection_GetCount(coll, &count);
+    if(SUCCEEDED(hr) && count > 0)
+    {
+        clear_devlist(list);
+        if(!VECTOR_RESERVE(*list, count))
+        {
+            IMMDeviceCollection_Release(coll);
+            return E_OUTOFMEMORY;
+        }
+
+        hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
+                                                         eMultimedia, &defdev);
+    }
+    if(SUCCEEDED(hr) && defdev != NULL)
+    {
+        defdevid = get_device_id(defdev);
+        if(defdevid)
+            add_device(defdev, defdevid, list);
+    }
+
+    for(i = 0;i < count;++i)
+    {
+        IMMDevice *device;
+        LPWSTR devid;
+
+        hr = IMMDeviceCollection_Item(coll, i, &device);
+        if(FAILED(hr)) continue;
+
+        devid = get_device_id(device);
+        if(devid)
+        {
+            if(wcscmp(devid, defdevid) != 0)
+                add_device(device, devid, list);
+            CoTaskMemFree(devid);
+        }
+        IMMDevice_Release(device);
+    }
+
+    if(defdev) IMMDevice_Release(defdev);
+    if(defdevid) CoTaskMemFree(defdevid);
+    IMMDeviceCollection_Release(coll);
+
+    return S_OK;
+}
+
+
+/* Proxy interface used by the message handler. */
+struct ALCmmdevProxyVtable;
+
+typedef struct ALCmmdevProxy {
+    const struct ALCmmdevProxyVtable *vtbl;
+} ALCmmdevProxy;
+
+struct ALCmmdevProxyVtable {
+    HRESULT (*const openProxy)(ALCmmdevProxy*);
+    void (*const closeProxy)(ALCmmdevProxy*);
+
+    HRESULT (*const resetProxy)(ALCmmdevProxy*);
+    HRESULT (*const startProxy)(ALCmmdevProxy*);
+    void  (*const stopProxy)(ALCmmdevProxy*);
+};
+
+#define DEFINE_ALCMMDEVPROXY_VTABLE(T)                                        \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy)                           \
+DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy)                             \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy)                          \
+DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy)                          \
+DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy)                              \
+                                                                              \
+static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = {          \
+    T##_ALCmmdevProxy_openProxy,                                              \
+    T##_ALCmmdevProxy_closeProxy,                                             \
+    T##_ALCmmdevProxy_resetProxy,                                             \
+    T##_ALCmmdevProxy_startProxy,                                             \
+    T##_ALCmmdevProxy_stopProxy,                                              \
+}
+
+static void ALCmmdevProxy_Construct(ALCmmdevProxy* UNUSED(self)) { }
+static void ALCmmdevProxy_Destruct(ALCmmdevProxy* UNUSED(self)) { }
+
+static DWORD CALLBACK ALCmmdevProxy_messageHandler(void *ptr)
+{
+    ThreadRequest *req = ptr;
+    IMMDeviceEnumerator *Enumerator;
+    ALuint deviceCount = 0;
+    ALCmmdevProxy *proxy;
+    HRESULT hr, cohr;
+    MSG msg;
+
+    TRACE("Starting message thread\n");
+
+    cohr = CoInitialize(NULL);
+    if(FAILED(cohr))
+    {
+        WARN("Failed to initialize COM: 0x%08lx\n", cohr);
+        ReturnMsgResponse(req, cohr);
+        return 0;
+    }
+
+    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+    if(FAILED(hr))
+    {
+        WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
+        CoUninitialize();
+        ReturnMsgResponse(req, hr);
+        return 0;
+    }
+    Enumerator = ptr;
+    IMMDeviceEnumerator_Release(Enumerator);
+    Enumerator = NULL;
+
+    CoUninitialize();
+
+    /* HACK: Force Windows to create a message queue for this thread before
+     * returning success, otherwise PostThreadMessage may fail if it gets
+     * called before GetMessage.
+     */
+    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+    TRACE("Message thread initialization complete\n");
+    ReturnMsgResponse(req, S_OK);
+
+    TRACE("Starting message loop\n");
+    while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
+    {
+        TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg.message, (void*)msg.lParam, (void*)msg.wParam);
+        switch(msg.message)
+        {
+        case WM_USER_OpenDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            hr = cohr = S_OK;
+            if(++deviceCount == 1)
+                hr = cohr = CoInitialize(NULL);
+            if(SUCCEEDED(hr))
+                hr = V0(proxy,openProxy)();
+            if(FAILED(hr))
+            {
+                if(--deviceCount == 0 && SUCCEEDED(cohr))
+                    CoUninitialize();
+            }
+
+            ReturnMsgResponse(req, hr);
+            continue;
+
+        case WM_USER_ResetDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            hr = V0(proxy,resetProxy)();
+            ReturnMsgResponse(req, hr);
+            continue;
+
+        case WM_USER_StartDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            hr = V0(proxy,startProxy)();
+            ReturnMsgResponse(req, hr);
+            continue;
+
+        case WM_USER_StopDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            V0(proxy,stopProxy)();
+            ReturnMsgResponse(req, S_OK);
+            continue;
+
+        case WM_USER_CloseDevice:
+            req = (ThreadRequest*)msg.wParam;
+            proxy = (ALCmmdevProxy*)msg.lParam;
+
+            V0(proxy,closeProxy)();
+            if(--deviceCount == 0)
+                CoUninitialize();
+
+            ReturnMsgResponse(req, S_OK);
+            continue;
+
+        case WM_USER_Enumerate:
+            req = (ThreadRequest*)msg.wParam;
+
+            hr = cohr = S_OK;
+            if(++deviceCount == 1)
+                hr = cohr = CoInitialize(NULL);
+            if(SUCCEEDED(hr))
+                hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+            if(SUCCEEDED(hr))
+            {
+                Enumerator = ptr;
+
+                if(msg.lParam == ALL_DEVICE_PROBE)
+                    hr = probe_devices(Enumerator, eRender, &PlaybackDevices);
+                else if(msg.lParam == CAPTURE_DEVICE_PROBE)
+                    hr = probe_devices(Enumerator, eCapture, &CaptureDevices);
+
+                IMMDeviceEnumerator_Release(Enumerator);
+                Enumerator = NULL;
+            }
+
+            if(--deviceCount == 0 && SUCCEEDED(cohr))
+                CoUninitialize();
+
+            ReturnMsgResponse(req, hr);
+            continue;
+
+        default:
+            ERR("Unexpected message: %u\n", msg.message);
+            continue;
+        }
+    }
+    TRACE("Message loop finished\n");
+
+    return 0;
+}
+
+
+typedef struct ALCmmdevPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
+    DERIVE_FROM_TYPE(ALCmmdevProxy);
+
+    WCHAR *devid;
+
+    IMMDevice *mmdev;
+    IAudioClient *client;
+    IAudioRenderClient *render;
+    HANDLE NotifyEvent;
+
+    HANDLE MsgEvent;
+
+    volatile UINT32 Padding;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCmmdevPlayback;
+
+static int ALCmmdevPlayback_mixerProc(void *arg);
+
+static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device);
+static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self);
+static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *name);
+static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_close(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self);
+static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self);
+static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self);
+static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self);
+static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self);
+static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self);
+static DECLARE_FORWARD2(ALCmmdevPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, ALCuint, availableSamples)
+static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self);
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCmmdevPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback)
+
+DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback);
+DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback);
+
+
+static void ALCmmdevPlayback_Construct(ALCmmdevPlayback *self, ALCdevice *device)
+{
+    SET_VTABLE2(ALCmmdevPlayback, ALCbackend, self);
+    SET_VTABLE2(ALCmmdevPlayback, ALCmmdevProxy, self);
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+
+    self->devid = NULL;
+
+    self->mmdev = NULL;
+    self->client = NULL;
+    self->render = NULL;
+    self->NotifyEvent = NULL;
+
+    self->MsgEvent = NULL;
+
+    self->Padding = 0;
+
+    self->killNow = 0;
+}
+
+static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback *self)
+{
+    if(self->NotifyEvent != NULL)
+        CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+    if(self->MsgEvent != NULL)
+        CloseHandle(self->MsgEvent);
+    self->MsgEvent = NULL;
+
+    free(self->devid);
+    self->devid = NULL;
+
+    ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+FORCE_ALIGN static int ALCmmdevPlayback_mixerProc(void *arg)
+{
+    ALCmmdevPlayback *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    UINT32 buffer_len, written;
+    ALuint update_size, len;
+    BYTE *buffer;
+    HRESULT hr;
+
+    hr = CoInitialize(NULL);
+    if(FAILED(hr))
+    {
+        ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
+        V0(device->Backend,lock)();
+        aluHandleDisconnect(device);
+        V0(device->Backend,unlock)();
+        return 1;
+    }
+
+    SetRTPriority();
+    althrd_setname(althrd_current(), MIXER_THREAD_NAME);
+
+    update_size = device->UpdateSize;
+    buffer_len = update_size * device->NumUpdates;
+    while(!self->killNow)
+    {
+        hr = IAudioClient_GetCurrentPadding(self->client, &written);
+        if(FAILED(hr))
+        {
+            ERR("Failed to get padding: 0x%08lx\n", hr);
+            V0(device->Backend,lock)();
+            aluHandleDisconnect(device);
+            V0(device->Backend,unlock)();
+            break;
+        }
+        self->Padding = written;
+
+        len = buffer_len - written;
+        if(len < update_size)
+        {
+            DWORD res;
+            res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
+            if(res != WAIT_OBJECT_0)
+                ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
+            continue;
+        }
+        len -= len%update_size;
+
+        hr = IAudioRenderClient_GetBuffer(self->render, len, &buffer);
+        if(SUCCEEDED(hr))
+        {
+            V0(device->Backend,lock)();
+            aluMixData(device, buffer, len);
+            self->Padding = written + len;
+            V0(device->Backend,unlock)();
+            hr = IAudioRenderClient_ReleaseBuffer(self->render, len, 0);
+        }
+        if(FAILED(hr))
+        {
+            ERR("Failed to buffer data: 0x%08lx\n", hr);
+            V0(device->Backend,lock)();
+            aluHandleDisconnect(device);
+            V0(device->Backend,unlock)();
+            break;
+        }
+    }
+    self->Padding = 0;
+
+    CoUninitialize();
+    return 0;
+}
+
+
+static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
+{
+    memset(out, 0, sizeof(*out));
+    if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+        *out = *(const WAVEFORMATEXTENSIBLE*)in;
+    else if(in->wFormatTag == WAVE_FORMAT_PCM)
+    {
+        out->Format = *in;
+        out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+        out->Format.cbSize = sizeof(*out) - sizeof(*in);
+        if(out->Format.nChannels == 1)
+            out->dwChannelMask = MONO;
+        else if(out->Format.nChannels == 2)
+            out->dwChannelMask = STEREO;
+        else
+            ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
+        out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+    }
+    else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
+    {
+        out->Format = *in;
+        out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+        out->Format.cbSize = sizeof(*out) - sizeof(*in);
+        if(out->Format.nChannels == 1)
+            out->dwChannelMask = MONO;
+        else if(out->Format.nChannels == 2)
+            out->dwChannelMask = STEREO;
+        else
+            ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
+        out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+    }
+    else
+    {
+        ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
+        return ALC_FALSE;
+    }
+    return ALC_TRUE;
+}
+
+
+static ALCenum ALCmmdevPlayback_open(ALCmmdevPlayback *self, const ALCchar *deviceName)
+{
+    HRESULT hr = S_OK;
+
+    self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
+    {
+        ERR("Failed to create message events: %lu\n", GetLastError());
+        hr = E_FAIL;
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        if(deviceName)
+        {
+            const DevMap *iter, *end;
+
+            if(VECTOR_SIZE(PlaybackDevices) == 0)
+            {
+                ThreadRequest req = { self->MsgEvent, 0 };
+                if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
+                    (void)WaitForResponse(&req);
+            }
+
+            hr = E_FAIL;
+            iter = VECTOR_ITER_BEGIN(PlaybackDevices);
+            end = VECTOR_ITER_END(PlaybackDevices);
+            for(;iter != end;iter++)
+            {
+                if(al_string_cmp_cstr(iter->name, deviceName) == 0)
+                {
+                    self->devid = strdupW(iter->devid);
+                    hr = S_OK;
+                    break;
+                }
+            }
+            if(FAILED(hr))
+                WARN("Failed to find device name matching \"%s\"\n", deviceName);
+        }
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        ThreadRequest req = { self->MsgEvent, 0 };
+
+        hr = E_FAIL;
+        if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+            hr = WaitForResponse(&req);
+        else
+            ERR("Failed to post thread message: %lu\n", GetLastError());
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->NotifyEvent != NULL)
+            CloseHandle(self->NotifyEvent);
+        self->NotifyEvent = NULL;
+        if(self->MsgEvent != NULL)
+            CloseHandle(self->MsgEvent);
+        self->MsgEvent = NULL;
+
+        free(self->devid);
+        self->devid = NULL;
+
+        ERR("Device init failed: 0x%08lx\n", hr);
+        return ALC_INVALID_VALUE;
+    }
+
+    return ALC_NO_ERROR;
+}
+
+static HRESULT ALCmmdevPlayback_openProxy(ALCmmdevPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    void *ptr;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        IMMDeviceEnumerator *Enumerator = ptr;
+        if(!self->devid)
+            hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &self->mmdev);
+        else
+            hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
+        IMMDeviceEnumerator_Release(Enumerator);
+        Enumerator = NULL;
+    }
+    if(SUCCEEDED(hr))
+        hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        self->client = ptr;
+        get_device_name(self->mmdev, &device->DeviceName);
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->mmdev)
+            IMMDevice_Release(self->mmdev);
+        self->mmdev = NULL;
+    }
+
+    return hr;
+}
+
+
+static void ALCmmdevPlayback_close(ALCmmdevPlayback *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+
+    if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        (void)WaitForResponse(&req);
+
+    CloseHandle(self->MsgEvent);
+    self->MsgEvent = NULL;
+
+    CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+
+    free(self->devid);
+    self->devid = NULL;
+}
+
+static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback *self)
+{
+    if(self->client)
+        IAudioClient_Release(self->client);
+    self->client = NULL;
+
+    if(self->mmdev)
+        IMMDevice_Release(self->mmdev);
+    self->mmdev = NULL;
+}
+
+
+static ALCboolean ALCmmdevPlayback_reset(ALCmmdevPlayback *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    HRESULT hr = E_FAIL;
+
+    if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        hr = WaitForResponse(&req);
+
+    return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
+}
+
+static HRESULT ALCmmdevPlayback_resetProxy(ALCmmdevPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    EndpointFormFactor formfactor = UnknownFormFactor;
+    WAVEFORMATEXTENSIBLE OutputType;
+    WAVEFORMATEX *wfx = NULL;
+    REFERENCE_TIME min_per, buf_time;
+    UINT32 buffer_len, min_len;
+    void *ptr = NULL;
+    HRESULT hr;
+
+    if(self->client)
+        IAudioClient_Release(self->client);
+    self->client = NULL;
+
+    hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+    if(FAILED(hr))
+    {
+        ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+    self->client = ptr;
+
+    hr = IAudioClient_GetMixFormat(self->client, &wfx);
+    if(FAILED(hr))
+    {
+        ERR("Failed to get mix format: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    if(!MakeExtensible(&OutputType, wfx))
+    {
+        CoTaskMemFree(wfx);
+        return E_FAIL;
+    }
+    CoTaskMemFree(wfx);
+    wfx = NULL;
+
+    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
+                                device->Frequency-1) / device->Frequency;
+
+    if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
+        device->Frequency = OutputType.Format.nSamplesPerSec;
+    if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
+    {
+        if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
+            device->FmtChans = DevFmtMono;
+        else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
+            device->FmtChans = DevFmtStereo;
+        else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
+            device->FmtChans = DevFmtQuad;
+        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
+            device->FmtChans = DevFmtX51;
+        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
+            device->FmtChans = DevFmtX51Rear;
+        else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
+            device->FmtChans = DevFmtX61;
+        else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
+            device->FmtChans = DevFmtX71;
+        else
+            ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
+    }
+
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+            OutputType.Format.nChannels = 1;
+            OutputType.dwChannelMask = MONO;
+            break;
+        case DevFmtBFormat3D:
+            device->FmtChans = DevFmtStereo;
+            /*fall-through*/
+        case DevFmtStereo:
+            OutputType.Format.nChannels = 2;
+            OutputType.dwChannelMask = STEREO;
+            break;
+        case DevFmtQuad:
+            OutputType.Format.nChannels = 4;
+            OutputType.dwChannelMask = QUAD;
+            break;
+        case DevFmtX51:
+            OutputType.Format.nChannels = 6;
+            OutputType.dwChannelMask = X5DOT1;
+            break;
+        case DevFmtX51Rear:
+            OutputType.Format.nChannels = 6;
+            OutputType.dwChannelMask = X5DOT1REAR;
+            break;
+        case DevFmtX61:
+            OutputType.Format.nChannels = 7;
+            OutputType.dwChannelMask = X6DOT1;
+            break;
+        case DevFmtX71:
+            OutputType.Format.nChannels = 8;
+            OutputType.dwChannelMask = X7DOT1;
+            break;
+    }
+    switch(device->FmtType)
+    {
+        case DevFmtByte:
+            device->FmtType = DevFmtUByte;
+            /* fall-through */
+        case DevFmtUByte:
+            OutputType.Format.wBitsPerSample = 8;
+            OutputType.Samples.wValidBitsPerSample = 8;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtUShort:
+            device->FmtType = DevFmtShort;
+            /* fall-through */
+        case DevFmtShort:
+            OutputType.Format.wBitsPerSample = 16;
+            OutputType.Samples.wValidBitsPerSample = 16;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtUInt:
+            device->FmtType = DevFmtInt;
+            /* fall-through */
+        case DevFmtInt:
+            OutputType.Format.wBitsPerSample = 32;
+            OutputType.Samples.wValidBitsPerSample = 32;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtFloat:
+            OutputType.Format.wBitsPerSample = 32;
+            OutputType.Samples.wValidBitsPerSample = 32;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+            break;
+    }
+    OutputType.Format.nSamplesPerSec = device->Frequency;
+
+    OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
+                                    OutputType.Format.wBitsPerSample / 8;
+    OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
+                                        OutputType.Format.nBlockAlign;
+
+    hr = IAudioClient_IsFormatSupported(self->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
+    if(FAILED(hr))
+    {
+        ERR("Failed to check format support: 0x%08lx\n", hr);
+        hr = IAudioClient_GetMixFormat(self->client, &wfx);
+    }
+    if(FAILED(hr))
+    {
+        ERR("Failed to find a supported format: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    if(wfx != NULL)
+    {
+        if(!MakeExtensible(&OutputType, wfx))
+        {
+            CoTaskMemFree(wfx);
+            return E_FAIL;
+        }
+        CoTaskMemFree(wfx);
+        wfx = NULL;
+
+        device->Frequency = OutputType.Format.nSamplesPerSec;
+        if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
+            device->FmtChans = DevFmtMono;
+        else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
+            device->FmtChans = DevFmtStereo;
+        else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
+            device->FmtChans = DevFmtQuad;
+        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
+            device->FmtChans = DevFmtX51;
+        else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1REAR)
+            device->FmtChans = DevFmtX51Rear;
+        else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
+            device->FmtChans = DevFmtX61;
+        else if(OutputType.Format.nChannels == 8 && (OutputType.dwChannelMask == X7DOT1 || OutputType.dwChannelMask == X7DOT1_WIDE))
+            device->FmtChans = DevFmtX71;
+        else
+        {
+            ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
+            device->FmtChans = DevFmtStereo;
+            OutputType.Format.nChannels = 2;
+            OutputType.dwChannelMask = STEREO;
+        }
+
+        if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
+        {
+            if(OutputType.Format.wBitsPerSample == 8)
+                device->FmtType = DevFmtUByte;
+            else if(OutputType.Format.wBitsPerSample == 16)
+                device->FmtType = DevFmtShort;
+            else if(OutputType.Format.wBitsPerSample == 32)
+                device->FmtType = DevFmtInt;
+            else
+            {
+                device->FmtType = DevFmtShort;
+                OutputType.Format.wBitsPerSample = 16;
+            }
+        }
+        else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+        {
+            device->FmtType = DevFmtFloat;
+            OutputType.Format.wBitsPerSample = 32;
+        }
+        else
+        {
+            ERR("Unhandled format sub-type\n");
+            device->FmtType = DevFmtShort;
+            OutputType.Format.wBitsPerSample = 16;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+        }
+        OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
+    }
+    get_device_formfactor(self->mmdev, &formfactor);
+    device->IsHeadphones = (device->FmtChans == DevFmtStereo && formfactor == Headphones);
+
+    SetDefaultWFXChannelOrder(device);
+
+    hr = IAudioClient_Initialize(self->client, AUDCLNT_SHAREMODE_SHARED,
+                                 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+                                 buf_time, 0, &OutputType.Format, NULL);
+    if(FAILED(hr))
+    {
+        ERR("Failed to initialize audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    hr = IAudioClient_GetDevicePeriod(self->client, &min_per, NULL);
+    if(SUCCEEDED(hr))
+    {
+        min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
+        /* Find the nearest multiple of the period size to the update size */
+        if(min_len < device->UpdateSize)
+            min_len *= (device->UpdateSize + min_len/2)/min_len;
+        hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
+    }
+    if(FAILED(hr))
+    {
+        ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    device->UpdateSize = min_len;
+    device->NumUpdates = buffer_len / device->UpdateSize;
+    if(device->NumUpdates <= 1)
+    {
+        ERR("Audio client returned buffer_len < period*2; expect break up\n");
+        device->NumUpdates = 2;
+        device->UpdateSize = buffer_len / device->NumUpdates;
+    }
+
+    hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
+    if(FAILED(hr))
+    {
+        ERR("Failed to set event handle: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    return hr;
+}
+
+
+static ALCboolean ALCmmdevPlayback_start(ALCmmdevPlayback *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    HRESULT hr = E_FAIL;
+
+    if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        hr = WaitForResponse(&req);
+
+    return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
+}
+
+static HRESULT ALCmmdevPlayback_startProxy(ALCmmdevPlayback *self)
+{
+    HRESULT hr;
+    void *ptr;
+
+    ResetEvent(self->NotifyEvent);
+    hr = IAudioClient_Start(self->client);
+    if(FAILED(hr))
+        ERR("Failed to start audio client: 0x%08lx\n", hr);
+
+    if(SUCCEEDED(hr))
+        hr = IAudioClient_GetService(self->client, &IID_IAudioRenderClient, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        self->render = ptr;
+        self->killNow = 0;
+        if(althrd_create(&self->thread, ALCmmdevPlayback_mixerProc, self) != althrd_success)
+        {
+            if(self->render)
+                IAudioRenderClient_Release(self->render);
+            self->render = NULL;
+            IAudioClient_Stop(self->client);
+            ERR("Failed to start thread\n");
+            hr = E_FAIL;
+        }
+    }
+
+    return hr;
+}
+
+
+static void ALCmmdevPlayback_stop(ALCmmdevPlayback *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        (void)WaitForResponse(&req);
+}
+
+static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback *self)
+{
+    int res;
+
+    if(!self->render)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    IAudioRenderClient_Release(self->render);
+    self->render = NULL;
+    IAudioClient_Stop(self->client);
+}
+
+
+static ALint64 ALCmmdevPlayback_getLatency(ALCmmdevPlayback *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    return (ALint64)self->Padding * 1000000000 / device->Frequency;
+}
+
+
+typedef struct ALCmmdevCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+    DERIVE_FROM_TYPE(ALCmmdevProxy);
+
+    WCHAR *devid;
+
+    IMMDevice *mmdev;
+    IAudioClient *client;
+    IAudioCaptureClient *capture;
+    HANDLE NotifyEvent;
+
+    HANDLE MsgEvent;
+
+    RingBuffer *Ring;
+
+    volatile int killNow;
+    althrd_t thread;
+} ALCmmdevCapture;
+
+static int ALCmmdevCapture_recordProc(void *arg);
+
+static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device);
+static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self);
+static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *name);
+static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self);
+static void ALCmmdevCapture_close(ALCmmdevCapture *self);
+static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self);
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALCboolean, reset)
+static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self);
+static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self);
+static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self);
+static void ALCmmdevCapture_stop(ALCmmdevCapture *self);
+static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self);
+static ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self);
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCmmdevCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture)
+
+DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture);
+DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture);
+
+
+static void ALCmmdevCapture_Construct(ALCmmdevCapture *self, ALCdevice *device)
+{
+    SET_VTABLE2(ALCmmdevCapture, ALCbackend, self);
+    SET_VTABLE2(ALCmmdevCapture, ALCmmdevProxy, self);
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy, self));
+
+    self->devid = NULL;
+
+    self->mmdev = NULL;
+    self->client = NULL;
+    self->capture = NULL;
+    self->NotifyEvent = NULL;
+
+    self->MsgEvent = NULL;
+
+    self->Ring = NULL;
+
+    self->killNow = 0;
+}
+
+static void ALCmmdevCapture_Destruct(ALCmmdevCapture *self)
+{
+    DestroyRingBuffer(self->Ring);
+    self->Ring = NULL;
+
+    if(self->NotifyEvent != NULL)
+        CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+    if(self->MsgEvent != NULL)
+        CloseHandle(self->MsgEvent);
+    self->MsgEvent = NULL;
+
+    free(self->devid);
+    self->devid = NULL;
+
+    ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy, self));
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+FORCE_ALIGN int ALCmmdevCapture_recordProc(void *arg)
+{
+    ALCmmdevCapture *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    HRESULT hr;
+
+    hr = CoInitialize(NULL);
+    if(FAILED(hr))
+    {
+        ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
+        V0(device->Backend,lock)();
+        aluHandleDisconnect(device);
+        V0(device->Backend,unlock)();
+        return 1;
+    }
+
+    althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+
+    while(!self->killNow)
+    {
+        UINT32 avail;
+        DWORD res;
+
+        hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
+        if(FAILED(hr))
+            ERR("Failed to get next packet size: 0x%08lx\n", hr);
+        else while(avail > 0 && SUCCEEDED(hr))
+        {
+            UINT32 numsamples;
+            DWORD flags;
+            BYTE *data;
+
+            hr = IAudioCaptureClient_GetBuffer(self->capture,
+                &data, &numsamples, &flags, NULL, NULL
+            );
+            if(FAILED(hr))
+            {
+                ERR("Failed to get capture buffer: 0x%08lx\n", hr);
+                break;
+            }
+
+            WriteRingBuffer(self->Ring, data, numsamples);
+
+            hr = IAudioCaptureClient_ReleaseBuffer(self->capture, numsamples);
+            if(FAILED(hr))
+            {
+                ERR("Failed to release capture buffer: 0x%08lx\n", hr);
+                break;
+            }
+
+            hr = IAudioCaptureClient_GetNextPacketSize(self->capture, &avail);
+            if(FAILED(hr))
+                ERR("Failed to get next packet size: 0x%08lx\n", hr);
+        }
+
+        if(FAILED(hr))
+        {
+            V0(device->Backend,lock)();
+            aluHandleDisconnect(device);
+            V0(device->Backend,unlock)();
+            break;
+        }
+
+        res = WaitForSingleObjectEx(self->NotifyEvent, 2000, FALSE);
+        if(res != WAIT_OBJECT_0)
+            ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
+    }
+
+    CoUninitialize();
+    return 0;
+}
+
+
+static ALCenum ALCmmdevCapture_open(ALCmmdevCapture *self, const ALCchar *deviceName)
+{
+    HRESULT hr = S_OK;
+
+    self->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    self->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if(self->NotifyEvent == NULL || self->MsgEvent == NULL)
+    {
+        ERR("Failed to create message events: %lu\n", GetLastError());
+        hr = E_FAIL;
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        if(deviceName)
+        {
+            const DevMap *iter;
+
+            if(VECTOR_SIZE(CaptureDevices) == 0)
+            {
+                ThreadRequest req = { self->MsgEvent, 0 };
+                if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, CAPTURE_DEVICE_PROBE))
+                    (void)WaitForResponse(&req);
+            }
+
+            hr = E_FAIL;
+#define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
+            VECTOR_FIND_IF(iter, const DevMap, CaptureDevices, MATCH_NAME);
+            if(iter == VECTOR_ITER_END(CaptureDevices))
+                WARN("Failed to find device name matching \"%s\"\n", deviceName);
+            else
+            {
+                self->devid = strdupW(iter->devid);
+                hr = S_OK;
+            }
+#undef MATCH_NAME
+        }
+    }
+
+    if(SUCCEEDED(hr))
+    {
+        ThreadRequest req = { self->MsgEvent, 0 };
+
+        hr = E_FAIL;
+        if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+            hr = WaitForResponse(&req);
+        else
+            ERR("Failed to post thread message: %lu\n", GetLastError());
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->NotifyEvent != NULL)
+            CloseHandle(self->NotifyEvent);
+        self->NotifyEvent = NULL;
+        if(self->MsgEvent != NULL)
+            CloseHandle(self->MsgEvent);
+        self->MsgEvent = NULL;
+
+        free(self->devid);
+        self->devid = NULL;
+
+        ERR("Device init failed: 0x%08lx\n", hr);
+        return ALC_INVALID_VALUE;
+    }
+    else
+    {
+        ThreadRequest req = { self->MsgEvent, 0 };
+
+        hr = E_FAIL;
+        if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+            hr = WaitForResponse(&req);
+        else
+            ERR("Failed to post thread message: %lu\n", GetLastError());
+
+        if(FAILED(hr))
+        {
+            ALCmmdevCapture_close(self);
+            if(hr == E_OUTOFMEMORY)
+               return ALC_OUT_OF_MEMORY;
+            return ALC_INVALID_VALUE;
+        }
+    }
+
+    return ALC_NO_ERROR;
+}
+
+static HRESULT ALCmmdevCapture_openProxy(ALCmmdevCapture *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    void *ptr;
+    HRESULT hr;
+
+    hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        IMMDeviceEnumerator *Enumerator = ptr;
+        if(!self->devid)
+            hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eCapture, eMultimedia, &self->mmdev);
+        else
+            hr = IMMDeviceEnumerator_GetDevice(Enumerator, self->devid, &self->mmdev);
+        IMMDeviceEnumerator_Release(Enumerator);
+        Enumerator = NULL;
+    }
+    if(SUCCEEDED(hr))
+        hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        self->client = ptr;
+        get_device_name(self->mmdev, &device->DeviceName);
+    }
+
+    if(FAILED(hr))
+    {
+        if(self->mmdev)
+            IMMDevice_Release(self->mmdev);
+        self->mmdev = NULL;
+    }
+
+    return hr;
+}
+
+
+static void ALCmmdevCapture_close(ALCmmdevCapture *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+
+    if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        (void)WaitForResponse(&req);
+
+    DestroyRingBuffer(self->Ring);
+    self->Ring = NULL;
+
+    CloseHandle(self->MsgEvent);
+    self->MsgEvent = NULL;
+
+    CloseHandle(self->NotifyEvent);
+    self->NotifyEvent = NULL;
+
+    free(self->devid);
+    self->devid = NULL;
+}
+
+static void ALCmmdevCapture_closeProxy(ALCmmdevCapture *self)
+{
+    if(self->client)
+        IAudioClient_Release(self->client);
+    self->client = NULL;
+
+    if(self->mmdev)
+        IMMDevice_Release(self->mmdev);
+    self->mmdev = NULL;
+}
+
+
+static HRESULT ALCmmdevCapture_resetProxy(ALCmmdevCapture *self)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    WAVEFORMATEXTENSIBLE OutputType;
+    REFERENCE_TIME buf_time;
+    UINT32 buffer_len;
+    void *ptr = NULL;
+    HRESULT hr;
+
+    if(self->client)
+        IAudioClient_Release(self->client);
+    self->client = NULL;
+
+    hr = IMMDevice_Activate(self->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
+    if(FAILED(hr))
+    {
+        ERR("Failed to reactivate audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+    self->client = ptr;
+
+    buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
+                                device->Frequency-1) / device->Frequency;
+
+    OutputType.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+    switch(device->FmtChans)
+    {
+        case DevFmtMono:
+            OutputType.Format.nChannels = 1;
+            OutputType.dwChannelMask = MONO;
+            break;
+        case DevFmtStereo:
+            OutputType.Format.nChannels = 2;
+            OutputType.dwChannelMask = STEREO;
+            break;
+        case DevFmtQuad:
+            OutputType.Format.nChannels = 4;
+            OutputType.dwChannelMask = QUAD;
+            break;
+        case DevFmtX51:
+            OutputType.Format.nChannels = 6;
+            OutputType.dwChannelMask = X5DOT1;
+            break;
+        case DevFmtX51Rear:
+            OutputType.Format.nChannels = 6;
+            OutputType.dwChannelMask = X5DOT1REAR;
+            break;
+        case DevFmtX61:
+            OutputType.Format.nChannels = 7;
+            OutputType.dwChannelMask = X6DOT1;
+            break;
+        case DevFmtX71:
+            OutputType.Format.nChannels = 8;
+            OutputType.dwChannelMask = X7DOT1;
+            break;
+
+        case DevFmtBFormat3D:
+            return E_FAIL;
+    }
+    switch(device->FmtType)
+    {
+        case DevFmtUByte:
+            OutputType.Format.wBitsPerSample = 8;
+            OutputType.Samples.wValidBitsPerSample = 8;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtShort:
+            OutputType.Format.wBitsPerSample = 16;
+            OutputType.Samples.wValidBitsPerSample = 16;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtInt:
+            OutputType.Format.wBitsPerSample = 32;
+            OutputType.Samples.wValidBitsPerSample = 32;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+            break;
+        case DevFmtFloat:
+            OutputType.Format.wBitsPerSample = 32;
+            OutputType.Samples.wValidBitsPerSample = 32;
+            OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
+            break;
+
+        case DevFmtByte:
+        case DevFmtUShort:
+        case DevFmtUInt:
+            WARN("%s capture samples not supported\n", DevFmtTypeString(device->FmtType));
+            return E_FAIL;
+    }
+    OutputType.Format.nSamplesPerSec = device->Frequency;
+
+    OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
+                                    OutputType.Format.wBitsPerSample / 8;
+    OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
+                                        OutputType.Format.nBlockAlign;
+
+    hr = IAudioClient_IsFormatSupported(self->client,
+        AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, NULL
+    );
+    if(FAILED(hr))
+    {
+        ERR("Failed to check format support: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    hr = IAudioClient_Initialize(self->client,
+        AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
+        buf_time, 0, &OutputType.Format, NULL
+    );
+    if(FAILED(hr))
+    {
+        ERR("Failed to initialize audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    hr = IAudioClient_GetBufferSize(self->client, &buffer_len);
+    if(FAILED(hr))
+    {
+        ERR("Failed to get buffer size: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    buffer_len = maxu(device->UpdateSize*device->NumUpdates, buffer_len);
+    self->Ring = CreateRingBuffer(OutputType.Format.nBlockAlign, buffer_len);
+    if(!self->Ring)
+    {
+        ERR("Failed to allocate capture ring buffer\n");
+        return E_OUTOFMEMORY;
+    }
+
+    hr = IAudioClient_SetEventHandle(self->client, self->NotifyEvent);
+    if(FAILED(hr))
+    {
+        ERR("Failed to set event handle: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    return hr;
+}
+
+
+static ALCboolean ALCmmdevCapture_start(ALCmmdevCapture *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    HRESULT hr = E_FAIL;
+
+    if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        hr = WaitForResponse(&req);
+
+    return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
+}
+
+static HRESULT ALCmmdevCapture_startProxy(ALCmmdevCapture *self)
+{
+    HRESULT hr;
+    void *ptr;
+
+    ResetEvent(self->NotifyEvent);
+    hr = IAudioClient_Start(self->client);
+    if(FAILED(hr))
+    {
+        ERR("Failed to start audio client: 0x%08lx\n", hr);
+        return hr;
+    }
+
+    hr = IAudioClient_GetService(self->client, &IID_IAudioCaptureClient, &ptr);
+    if(SUCCEEDED(hr))
+    {
+        self->capture = ptr;
+        self->killNow = 0;
+        if(althrd_create(&self->thread, ALCmmdevCapture_recordProc, self) != althrd_success)
+        {
+            ERR("Failed to start thread\n");
+            IAudioCaptureClient_Release(self->capture);
+            self->capture = NULL;
+            hr = E_FAIL;
+        }
+    }
+
+    if(FAILED(hr))
+    {
+        IAudioClient_Stop(self->client);
+        IAudioClient_Reset(self->client);
+    }
+
+    return hr;
+}
+
+
+static void ALCmmdevCapture_stop(ALCmmdevCapture *self)
+{
+    ThreadRequest req = { self->MsgEvent, 0 };
+    if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)STATIC_CAST(ALCmmdevProxy, self)))
+        (void)WaitForResponse(&req);
+}
+
+static void ALCmmdevCapture_stopProxy(ALCmmdevCapture *self)
+{
+    int res;
+
+    if(!self->capture)
+        return;
+
+    self->killNow = 1;
+    althrd_join(self->thread, &res);
+
+    IAudioCaptureClient_Release(self->capture);
+    self->capture = NULL;
+    IAudioClient_Stop(self->client);
+    IAudioClient_Reset(self->client);
+}
+
+
+ALuint ALCmmdevCapture_availableSamples(ALCmmdevCapture *self)
+{
+    return RingBufferSize(self->Ring);
+}
+
+ALCenum ALCmmdevCapture_captureSamples(ALCmmdevCapture *self, ALCvoid *buffer, ALCuint samples)
+{
+    if(ALCmmdevCapture_availableSamples(self) < samples)
+        return ALC_INVALID_VALUE;
+    ReadRingBuffer(self->Ring, buffer, samples);
+    return ALC_NO_ERROR;
+}
+
+
+static inline void AppendAllDevicesList2(const DevMap *entry)
+{ AppendAllDevicesList(al_string_get_cstr(entry->name)); }
+static inline void AppendCaptureDeviceList2(const DevMap *entry)
+{ AppendCaptureDeviceList(al_string_get_cstr(entry->name)); }
+
+typedef struct ALCmmdevBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCmmdevBackendFactory;
+#define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
+
+static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory *self);
+static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory *self);
+static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory *self, ALCbackend_Type type);
+static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory);
+
+
+static BOOL MMDevApiLoad(void)
+{
+    static HRESULT InitResult;
+    if(!ThreadHdl)
+    {
+        ThreadRequest req;
+        InitResult = E_FAIL;
+
+        req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+        if(req.FinishedEvt == NULL)
+            ERR("Failed to create event: %lu\n", GetLastError());
+        else
+        {
+            ThreadHdl = CreateThread(NULL, 0, ALCmmdevProxy_messageHandler, &req, 0, &ThreadID);
+            if(ThreadHdl != NULL)
+                InitResult = WaitForResponse(&req);
+            CloseHandle(req.FinishedEvt);
+        }
+    }
+    return SUCCEEDED(InitResult);
+}
+
+static ALCboolean ALCmmdevBackendFactory_init(ALCmmdevBackendFactory* UNUSED(self))
+{
+    VECTOR_INIT(PlaybackDevices);
+    VECTOR_INIT(CaptureDevices);
+
+    if(!MMDevApiLoad())
+        return ALC_FALSE;
+    return ALC_TRUE;
+}
+
+static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory* UNUSED(self))
+{
+    clear_devlist(&PlaybackDevices);
+    VECTOR_DEINIT(PlaybackDevices);
+
+    clear_devlist(&CaptureDevices);
+    VECTOR_DEINIT(CaptureDevices);
+
+    if(ThreadHdl)
+    {
+        TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
+        PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
+        CloseHandle(ThreadHdl);
+        ThreadHdl = NULL;
+    }
+}
+
+static ALCboolean ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback/* || type == ALCbackend_Capture*/)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory* UNUSED(self), enum DevProbe type)
+{
+    ThreadRequest req = { NULL, 0 };
+
+    req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
+    if(req.FinishedEvt == NULL)
+        ERR("Failed to create event: %lu\n", GetLastError());
+    else
+    {
+        HRESULT hr = E_FAIL;
+        if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
+            hr = WaitForResponse(&req);
+        if(SUCCEEDED(hr)) switch(type)
+        {
+        case ALL_DEVICE_PROBE:
+            VECTOR_FOR_EACH(const DevMap, PlaybackDevices, AppendAllDevicesList2);
+            break;
+
+        case CAPTURE_DEVICE_PROBE:
+            VECTOR_FOR_EACH(const DevMap, CaptureDevices, AppendCaptureDeviceList2);
+            break;
+        }
+        CloseHandle(req.FinishedEvt);
+        req.FinishedEvt = NULL;
+    }
+}
+
+static ALCbackend* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCmmdevPlayback *backend;
+
+        backend = ALCmmdevPlayback_New(sizeof(*backend));
+        if(!backend) return NULL;
+        memset(backend, 0, sizeof(*backend));
+
+        ALCmmdevPlayback_Construct(backend, device);
+
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCmmdevCapture *backend;
+
+        backend = ALCmmdevCapture_New(sizeof(*backend));
+        if(!backend) return NULL;
+        memset(backend, 0, sizeof(*backend));
+
+        ALCmmdevCapture_Construct(backend, device);
+
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+
+ALCbackendFactory *ALCmmdevBackendFactory_getFactory(void)
+{
+    static ALCmmdevBackendFactory factory = ALCMMDEVBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}
diff --git a/Telegram/_openal_patch/Alc/backends/winmm.c b/Telegram/_openal_patch/Alc/backends/winmm.c
index 81ce905efb..77212c2b3c 100644
--- a/Telegram/_openal_patch/Alc/backends/winmm.c
+++ b/Telegram/_openal_patch/Alc/backends/winmm.c
@@ -31,30 +31,13 @@
 #include "alu.h"
 #include "threads.h"
 
+#include "backends/base.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;
@@ -155,111 +138,117 @@ static void ProbeCaptureDevices(void)
 }
 
 
-/*
-    WaveOutProc
+typedef struct ALCwinmmPlayback {
+    DERIVE_FROM_TYPE(ALCbackend);
 
-    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))
+    RefCount WaveBuffersCommitted;
+    WAVEHDR WaveBuffer[4];
+
+    HWAVEOUT OutHdl;
+
+    WAVEFORMATEX Format;
+
+    volatile ALboolean killNow;
+    althrd_t thread;
+} ALCwinmmPlayback;
+
+static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device);
+static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self);
+
+static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
+static int ALCwinmmPlayback_mixerProc(void *arg);
+
+static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *name);
+static void ALCwinmmPlayback_close(ALCwinmmPlayback *self);
+static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self);
+static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self);
+static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self);
+static DECLARE_FORWARD2(ALCwinmmPlayback, ALCbackend, ALCenum, captureSamples, ALCvoid*, ALCuint)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALCuint, availableSamples)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwinmmPlayback, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwinmmPlayback)
+
+DEFINE_ALCBACKEND_VTABLE(ALCwinmmPlayback);
+
+
+static void ALCwinmmPlayback_Construct(ALCwinmmPlayback *self, ALCdevice *device)
 {
-    ALCdevice *Device = (ALCdevice*)instance;
-    WinMMData *data = Device->ExtraData;
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCwinmmPlayback, ALCbackend, self);
+
+    InitRef(&self->WaveBuffersCommitted, 0);
+    self->OutHdl = NULL;
+
+    self->killNow = AL_TRUE;
+}
+
+static void ALCwinmmPlayback_Destruct(ALCwinmmPlayback *self)
+{
+    if(self->OutHdl)
+        waveOutClose(self->OutHdl);
+    self->OutHdl = 0;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+/* ALCwinmmPlayback_waveOutProc
+ *
+ * Posts a message to 'ALCwinmmPlayback_mixerProc' everytime a WaveOut Buffer
+ * is completed and returns to the application (for more data)
+ */
+static void CALLBACK ALCwinmmPlayback_waveOutProc(HWAVEOUT UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
+{
+    ALCwinmmPlayback *self = (ALCwinmmPlayback*)instance;
 
     if(msg != WOM_DONE)
         return;
 
-    DecrementRef(&data->WaveBuffersCommitted);
-    PostThreadMessage(data->thread, msg, 0, param1);
+    DecrementRef(&self->WaveBuffersCommitted);
+    PostThreadMessage(self->thread, msg, 0, param1);
 }
 
-FORCE_ALIGN static int PlaybackThreadProc(void *arg)
+FORCE_ALIGN static int ALCwinmmPlayback_mixerProc(void *arg)
 {
-    ALCdevice *Device = (ALCdevice*)arg;
-    WinMMData *data = Device->ExtraData;
+    ALCwinmmPlayback *self = arg;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
     WAVEHDR *WaveHdr;
     MSG msg;
 
     SetRTPriority();
     althrd_setname(althrd_current(), MIXER_THREAD_NAME);
 
-    while(GetMessage(&msg, NULL, 0, 0))
+	if (!self->killNow) while (GetMessage(&msg, NULL, 0, 0))
     {
         if(msg.message != WOM_DONE)
             continue;
 
-        if(data->killNow)
+        if(self->killNow)
         {
-            if(ReadRef(&data->WaveBuffersCommitted) == 0)
+            if(ReadRef(&self->WaveBuffersCommitted) == 0)
                 break;
             continue;
         }
 
         WaveHdr = ((WAVEHDR*)msg.lParam);
-        aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength /
-                                            data->Format.nBlockAlign);
+        aluMixData(device, WaveHdr->lpData, WaveHdr->dwBufferLength /
+                                            self->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);
+        waveOutWrite(self->OutHdl, WaveHdr, sizeof(WAVEHDR));
+        IncrementRef(&self->WaveBuffersCommitted);
     }
 
     return 0;
 }
 
 
-static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
+static ALCenum ALCwinmmPlayback_open(ALCwinmmPlayback *self, const ALCchar *deviceName)
 {
-    WinMMData *data = NULL;
-    const al_string *iter, *end;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const al_string *iter;
     UINT DeviceID;
     MMRESULT res;
 
@@ -267,129 +256,109 @@ static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
         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)
+#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) && \
+                             (!deviceName || al_string_cmp_cstr(*(iter), deviceName) == 0))
+    VECTOR_FIND_IF(iter, const al_string, PlaybackDevices, MATCH_DEVNAME);
+    if(iter == VECTOR_ITER_END(PlaybackDevices))
         return ALC_INVALID_VALUE;
+#undef MATCH_DEVNAME
 
-    data = calloc(1, sizeof(*data));
-    if(!data)
-        return ALC_OUT_OF_MEMORY;
-    Device->ExtraData = data;
+    DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(PlaybackDevices));
 
 retry_open:
-    memset(&data->Format, 0, sizeof(WAVEFORMATEX));
-    if(Device->FmtType == DevFmtFloat)
+    memset(&self->Format, 0, sizeof(WAVEFORMATEX));
+    if(device->FmtType == DevFmtFloat)
     {
-        data->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
-        data->Format.wBitsPerSample = 32;
+        self->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
+        self->Format.wBitsPerSample = 32;
     }
     else
     {
-        data->Format.wFormatTag = WAVE_FORMAT_PCM;
-        if(Device->FmtType == DevFmtUByte || Device->FmtType == DevFmtByte)
-            data->Format.wBitsPerSample = 8;
+        self->Format.wFormatTag = WAVE_FORMAT_PCM;
+        if(device->FmtType == DevFmtUByte || device->FmtType == DevFmtByte)
+            self->Format.wBitsPerSample = 8;
         else
-            data->Format.wBitsPerSample = 16;
+            self->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;
+    self->Format.nChannels = ((device->FmtChans == DevFmtMono) ? 1 : 2);
+    self->Format.nBlockAlign = self->Format.wBitsPerSample *
+                               self->Format.nChannels / 8;
+    self->Format.nSamplesPerSec = device->Frequency;
+    self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
+                                   self->Format.nBlockAlign;
+    self->Format.cbSize = 0;
 
-    if((res=waveOutOpen(&data->WaveHandle.Out, DeviceID, &data->Format, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
+    if((res=waveOutOpen(&self->OutHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmPlayback_waveOutProc, (DWORD_PTR)self, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
     {
-        if(Device->FmtType == DevFmtFloat)
+        if(device->FmtType == DevFmtFloat)
         {
-            Device->FmtType = DevFmtShort;
+            device->FmtType = DevFmtShort;
             goto retry_open;
         }
         ERR("waveOutOpen failed: %u\n", res);
         goto failure;
     }
 
-    al_string_copy(&Device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
+    al_string_copy(&device->DeviceName, VECTOR_ELEM(PlaybackDevices, DeviceID));
     return ALC_NO_ERROR;
 
 failure:
-    if(data->WaveHandle.Out)
-        waveOutClose(data->WaveHandle.Out);
+    if(self->OutHdl)
+        waveOutClose(self->OutHdl);
+    self->OutHdl = NULL;
 
-    free(data);
-    Device->ExtraData = NULL;
     return ALC_INVALID_VALUE;
 }
 
-static void WinMMClosePlayback(ALCdevice *device)
+static void ALCwinmmPlayback_close(ALCwinmmPlayback* UNUSED(self))
+{ }
+
+static ALCboolean ALCwinmmPlayback_reset(ALCwinmmPlayback *self)
 {
-    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;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
 
     device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
-                                  data->Format.nSamplesPerSec /
+                                  self->Format.nSamplesPerSec /
                                   device->Frequency);
     device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
     device->NumUpdates = 4;
-    device->Frequency = data->Format.nSamplesPerSec;
+    device->Frequency = self->Format.nSamplesPerSec;
 
-    if(data->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
+    if(self->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
     {
-        if(data->Format.wBitsPerSample == 32)
+        if(self->Format.wBitsPerSample == 32)
             device->FmtType = DevFmtFloat;
         else
         {
-            ERR("Unhandled IEEE float sample depth: %d\n", data->Format.wBitsPerSample);
+            ERR("Unhandled IEEE float sample depth: %d\n", self->Format.wBitsPerSample);
             return ALC_FALSE;
         }
     }
-    else if(data->Format.wFormatTag == WAVE_FORMAT_PCM)
+    else if(self->Format.wFormatTag == WAVE_FORMAT_PCM)
     {
-        if(data->Format.wBitsPerSample == 16)
+        if(self->Format.wBitsPerSample == 16)
             device->FmtType = DevFmtShort;
-        else if(data->Format.wBitsPerSample == 8)
+        else if(self->Format.wBitsPerSample == 8)
             device->FmtType = DevFmtUByte;
         else
         {
-            ERR("Unhandled PCM sample depth: %d\n", data->Format.wBitsPerSample);
+            ERR("Unhandled PCM sample depth: %d\n", self->Format.wBitsPerSample);
             return ALC_FALSE;
         }
     }
     else
     {
-        ERR("Unhandled format tag: 0x%04x\n", data->Format.wFormatTag);
+        ERR("Unhandled format tag: 0x%04x\n", self->Format.wFormatTag);
         return ALC_FALSE;
     }
 
-    if(data->Format.nChannels == 2)
+    if(self->Format.nChannels == 2)
         device->FmtChans = DevFmtStereo;
-    else if(data->Format.nChannels == 1)
+    else if(self->Format.nChannels == 1)
         device->FmtChans = DevFmtMono;
     else
     {
-        ERR("Unhandled channel count: %d\n", data->Format.nChannels);
+        ERR("Unhandled channel count: %d\n", self->Format.nChannels);
         return ALC_FALSE;
     }
     SetDefaultWFXChannelOrder(device);
@@ -397,18 +366,18 @@ static ALCboolean WinMMResetPlayback(ALCdevice *device)
     return ALC_TRUE;
 }
 
-static ALCboolean WinMMStartPlayback(ALCdevice *device)
+static ALCboolean ALCwinmmPlayback_start(ALCwinmmPlayback *self)
 {
-    WinMMData *data = (WinMMData*)device->ExtraData;
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
     ALbyte *BufferData;
     ALint BufferSize;
     ALuint i;
 
-    data->killNow = AL_FALSE;
-    if(althrd_create(&data->thread, PlaybackThreadProc, device) != althrd_success)
+    self->killNow = AL_FALSE;
+    if(althrd_create(&self->thread, ALCwinmmPlayback_mixerProc, self) != althrd_success)
         return ALC_FALSE;
 
-    InitRef(&data->WaveBuffersCommitted, 0);
+    InitRef(&self->WaveBuffersCommitted, 0);
 
     // Create 4 Buffers
     BufferSize  = device->UpdateSize*device->NumUpdates / 4;
@@ -417,49 +386,153 @@ static ALCboolean WinMMStartPlayback(ALCdevice *device)
     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);
+        memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
+        self->WaveBuffer[i].dwBufferLength = BufferSize;
+        self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
+                                      (self->WaveBuffer[i-1].lpData +
+                                       self->WaveBuffer[i-1].dwBufferLength));
+        waveOutPrepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        waveOutWrite(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        IncrementRef(&self->WaveBuffersCommitted);
     }
 
     return ALC_TRUE;
 }
 
-static void WinMMStopPlayback(ALCdevice *device)
+static void ALCwinmmPlayback_stop(ALCwinmmPlayback *self)
 {
-    WinMMData *data = (WinMMData*)device->ExtraData;
     void *buffer = NULL;
     int i;
 
-    if(data->killNow)
+    if(self->killNow)
         return;
 
     // Set flag to stop processing headers
-    data->killNow = AL_TRUE;
-    althrd_join(data->thread, &i);
+    self->killNow = AL_TRUE;
+    althrd_join(self->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;
+        waveOutUnprepareHeader(self->OutHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        if(i == 0) buffer = self->WaveBuffer[i].lpData;
+        self->WaveBuffer[i].lpData = NULL;
     }
     free(buffer);
 }
 
 
-static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
+
+typedef struct ALCwinmmCapture {
+    DERIVE_FROM_TYPE(ALCbackend);
+
+    RefCount WaveBuffersCommitted;
+    WAVEHDR WaveBuffer[4];
+
+    HWAVEIN InHdl;
+
+    RingBuffer *Ring;
+
+    WAVEFORMATEX Format;
+
+    volatile ALboolean killNow;
+    althrd_t thread;
+} ALCwinmmCapture;
+
+static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device);
+static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self);
+
+static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2);
+static int ALCwinmmCapture_captureProc(void *arg);
+
+static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name);
+static void ALCwinmmCapture_close(ALCwinmmCapture *self);
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALCboolean, reset)
+static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self);
+static void ALCwinmmCapture_stop(ALCwinmmCapture *self);
+static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples);
+static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self);
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, ALint64, getLatency)
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, lock)
+static DECLARE_FORWARD(ALCwinmmCapture, ALCbackend, void, unlock)
+DECLARE_DEFAULT_ALLOCATORS(ALCwinmmCapture)
+
+DEFINE_ALCBACKEND_VTABLE(ALCwinmmCapture);
+
+
+static void ALCwinmmCapture_Construct(ALCwinmmCapture *self, ALCdevice *device)
 {
-    const al_string *iter, *end;
+    ALCbackend_Construct(STATIC_CAST(ALCbackend, self), device);
+    SET_VTABLE2(ALCwinmmCapture, ALCbackend, self);
+
+    InitRef(&self->WaveBuffersCommitted, 0);
+    self->InHdl = NULL;
+
+    self->killNow = AL_TRUE;
+}
+
+static void ALCwinmmCapture_Destruct(ALCwinmmCapture *self)
+{
+    if(self->InHdl)
+        waveInClose(self->InHdl);
+    self->InHdl = 0;
+
+    ALCbackend_Destruct(STATIC_CAST(ALCbackend, self));
+}
+
+
+/* ALCwinmmCapture_waveInProc
+ *
+ * Posts a message to 'ALCwinmmCapture_captureProc' everytime a WaveIn Buffer
+ * is completed and returns to the application (with more data).
+ */
+static void CALLBACK ALCwinmmCapture_waveInProc(HWAVEIN UNUSED(device), UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR UNUSED(param2))
+{
+    ALCwinmmCapture *self = (ALCwinmmCapture*)instance;
+
+    if(msg != WIM_DATA)
+        return;
+
+    DecrementRef(&self->WaveBuffersCommitted);
+    PostThreadMessage(self->thread, msg, 0, param1);
+}
+
+static int ALCwinmmCapture_captureProc(void *arg)
+{
+    ALCwinmmCapture *self = arg;
+    WAVEHDR *WaveHdr;
+    MSG msg;
+
+    althrd_setname(althrd_current(), RECORD_THREAD_NAME);
+
+    if (!self->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(self->killNow)
+            break;
+
+        WaveHdr = ((WAVEHDR*)msg.lParam);
+        WriteRingBuffer(self->Ring, (ALubyte*)WaveHdr->lpData,
+                        WaveHdr->dwBytesRecorded/self->Format.nBlockAlign);
+
+        // Send buffer back to capture more data
+        waveInAddBuffer(self->InHdl, WaveHdr, sizeof(WAVEHDR));
+        IncrementRef(&self->WaveBuffersCommitted);
+    }
+
+    return 0;
+}
+
+
+static ALCenum ALCwinmmCapture_open(ALCwinmmCapture *self, const ALCchar *name)
+{
+    ALCdevice *device = STATIC_CAST(ALCbackend, self)->mDevice;
+    const al_string *iter;
     ALbyte *BufferData = NULL;
     DWORD CapturedDataSize;
-    WinMMData *data = NULL;
     ALint BufferSize;
     UINT DeviceID;
     MMRESULT res;
@@ -469,21 +542,15 @@ static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
         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)
+#define MATCH_DEVNAME(iter) (!al_string_empty(*(iter)) &&  (!name || al_string_cmp_cstr(*iter, name) == 0))
+    VECTOR_FIND_IF(iter, const al_string, CaptureDevices, MATCH_DEVNAME);
+    if(iter == VECTOR_ITER_END(CaptureDevices))
         return ALC_INVALID_VALUE;
+#undef MATCH_DEVNAME
 
-    switch(Device->FmtChans)
+    DeviceID = (UINT)(iter - VECTOR_ITER_BEGIN(CaptureDevices));
+
+    switch(device->FmtChans)
     {
         case DevFmtMono:
         case DevFmtStereo:
@@ -491,13 +558,14 @@ static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
 
         case DevFmtQuad:
         case DevFmtX51:
-        case DevFmtX51Side:
+        case DevFmtX51Rear:
         case DevFmtX61:
         case DevFmtX71:
+        case DevFmtBFormat3D:
             return ALC_INVALID_ENUM;
     }
 
-    switch(Device->FmtType)
+    switch(device->FmtType)
     {
         case DevFmtUByte:
         case DevFmtShort:
@@ -511,147 +579,134 @@ static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
             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) ?
+    memset(&self->Format, 0, sizeof(WAVEFORMATEX));
+    self->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;
+    self->Format.nChannels = ChannelsFromDevFmt(device->FmtChans);
+    self->Format.wBitsPerSample = BytesFromDevFmt(device->FmtType) * 8;
+    self->Format.nBlockAlign = self->Format.wBitsPerSample *
+                               self->Format.nChannels / 8;
+    self->Format.nSamplesPerSec = device->Frequency;
+    self->Format.nAvgBytesPerSec = self->Format.nSamplesPerSec *
+                                   self->Format.nBlockAlign;
+    self->Format.cbSize = 0;
 
-    if((res=waveInOpen(&data->WaveHandle.In, DeviceID, &data->Format, (DWORD_PTR)&WaveInProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
+    if((res=waveInOpen(&self->InHdl, DeviceID, &self->Format, (DWORD_PTR)&ALCwinmmCapture_waveInProc, (DWORD_PTR)self, 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;
+    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;
+    if(CapturedDataSize < (self->Format.nSamplesPerSec / 10))
+        CapturedDataSize = self->Format.nSamplesPerSec / 10;
 
-    data->Ring = CreateRingBuffer(data->Format.nBlockAlign, CapturedDataSize);
-    if(!data->Ring)
-        goto failure;
+    self->Ring = CreateRingBuffer(self->Format.nBlockAlign, CapturedDataSize);
+    if(!self->Ring) goto failure;
 
-    InitRef(&data->WaveBuffersCommitted, 0);
+    InitRef(&self->WaveBuffersCommitted, 0);
 
     // Create 4 Buffers of 50ms each
-    BufferSize = data->Format.nAvgBytesPerSec / 20;
-    BufferSize -= (BufferSize % data->Format.nBlockAlign);
+    BufferSize = self->Format.nAvgBytesPerSec / 20;
+    BufferSize -= (BufferSize % self->Format.nBlockAlign);
 
     BufferData = calloc(4, BufferSize);
-    if(!BufferData)
-        goto failure;
+    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);
+        memset(&self->WaveBuffer[i], 0, sizeof(WAVEHDR));
+        self->WaveBuffer[i].dwBufferLength = BufferSize;
+        self->WaveBuffer[i].lpData = ((i==0) ? (CHAR*)BufferData :
+                                      (self->WaveBuffer[i-1].lpData +
+                                       self->WaveBuffer[i-1].dwBufferLength));
+        self->WaveBuffer[i].dwFlags = 0;
+        self->WaveBuffer[i].dwLoops = 0;
+        waveInPrepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        waveInAddBuffer(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        IncrementRef(&self->WaveBuffersCommitted);
     }
 
-    if(althrd_create(&data->thread, CaptureThreadProc, Device) != althrd_success)
+    self->killNow = AL_FALSE;
+    if(althrd_create(&self->thread, ALCwinmmCapture_captureProc, self) != althrd_success)
         goto failure;
 
-    al_string_copy(&Device->DeviceName, VECTOR_ELEM(CaptureDevices, DeviceID));
+    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));
+            waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
         free(BufferData);
     }
 
-    if(data->Ring)
-        DestroyRingBuffer(data->Ring);
+    if(self->Ring)
+        DestroyRingBuffer(self->Ring);
+    self->Ring = NULL;
 
-    if(data->WaveHandle.In)
-        waveInClose(data->WaveHandle.In);
+    if(self->InHdl)
+        waveInClose(self->InHdl);
+    self->InHdl = NULL;
 
-    free(data);
-    Device->ExtraData = NULL;
     return ALC_INVALID_VALUE;
 }
 
-static void WinMMCloseCapture(ALCdevice *Device)
+static void ALCwinmmCapture_close(ALCwinmmCapture *self)
 {
-    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);
+    self->killNow = AL_TRUE;
+    PostThreadMessage(self->thread, WM_QUIT, 0, 0);
 
-    althrd_join(data->thread, &i);
+    althrd_join(self->thread, &i);
 
     /* Make sure capture is stopped and all pending buffers are flushed. */
-    waveInReset(data->WaveHandle.In);
+    waveInReset(self->InHdl);
 
     // 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;
+        waveInUnprepareHeader(self->InHdl, &self->WaveBuffer[i], sizeof(WAVEHDR));
+        if(i == 0) buffer = self->WaveBuffer[i].lpData;
+        self->WaveBuffer[i].lpData = NULL;
     }
     free(buffer);
 
-    DestroyRingBuffer(data->Ring);
-    data->Ring = NULL;
+    DestroyRingBuffer(self->Ring);
+    self->Ring = NULL;
 
     // Close the Wave device
-    waveInClose(data->WaveHandle.In);
-    data->WaveHandle.In = 0;
-
-    free(data);
-    Device->ExtraData = NULL;
+    waveInClose(self->InHdl);
+    self->InHdl = NULL;
 }
 
-static void WinMMStartCapture(ALCdevice *Device)
+static ALCboolean ALCwinmmCapture_start(ALCwinmmCapture *self)
 {
-    WinMMData *data = (WinMMData*)Device->ExtraData;
-    waveInStart(data->WaveHandle.In);
+    waveInStart(self->InHdl);
+    return ALC_TRUE;
 }
 
-static void WinMMStopCapture(ALCdevice *Device)
+static void ALCwinmmCapture_stop(ALCwinmmCapture *self)
 {
-    WinMMData *data = (WinMMData*)Device->ExtraData;
-    waveInStop(data->WaveHandle.In);
+    waveInStop(self->InHdl);
 }
 
-static ALCenum WinMMCaptureSamples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
+static ALCenum ALCwinmmCapture_captureSamples(ALCwinmmCapture *self, ALCvoid *buffer, ALCuint samples)
 {
-    WinMMData *data = (WinMMData*)Device->ExtraData;
-    ReadRingBuffer(data->Ring, Buffer, Samples);
+    ReadRingBuffer(self->Ring, buffer, samples);
     return ALC_NO_ERROR;
 }
 
-static ALCuint WinMMAvailableSamples(ALCdevice *Device)
+static ALCuint ALCwinmmCapture_availableSamples(ALCwinmmCapture *self)
 {
-    WinMMData *data = (WinMMData*)Device->ExtraData;
-    return RingBufferSize(data->Ring);
+    return RingBufferSize(self->Ring);
 }
 
 
@@ -666,31 +721,29 @@ static inline void AppendCaptureDeviceList2(const al_string *name)
         AppendCaptureDeviceList(al_string_get_cstr(*name));
 }
 
-static const BackendFuncs WinMMFuncs = {
-    WinMMOpenPlayback,
-    WinMMClosePlayback,
-    WinMMResetPlayback,
-    WinMMStartPlayback,
-    WinMMStopPlayback,
-    WinMMOpenCapture,
-    WinMMCloseCapture,
-    WinMMStartCapture,
-    WinMMStopCapture,
-    WinMMCaptureSamples,
-    WinMMAvailableSamples,
-    ALCdevice_GetLatencyDefault
-};
+typedef struct ALCwinmmBackendFactory {
+    DERIVE_FROM_TYPE(ALCbackendFactory);
+} ALCwinmmBackendFactory;
+#define ALCWINMMBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwinmmBackendFactory, ALCbackendFactory) } }
 
-ALCboolean alcWinMMInit(BackendFuncs *FuncList)
+static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory *self);
+static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory *self);
+static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory *self, ALCbackend_Type type);
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory *self, enum DevProbe type);
+static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory *self, ALCdevice *device, ALCbackend_Type type);
+
+DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwinmmBackendFactory);
+
+
+static ALCboolean ALCwinmmBackendFactory_init(ALCwinmmBackendFactory* UNUSED(self))
 {
     VECTOR_INIT(PlaybackDevices);
     VECTOR_INIT(CaptureDevices);
 
-    *FuncList = WinMMFuncs;
     return ALC_TRUE;
 }
 
-void alcWinMMDeinit()
+static void ALCwinmmBackendFactory_deinit(ALCwinmmBackendFactory* UNUSED(self))
 {
     clear_devlist(&PlaybackDevices);
     VECTOR_DEINIT(PlaybackDevices);
@@ -699,7 +752,14 @@ void alcWinMMDeinit()
     VECTOR_DEINIT(CaptureDevices);
 }
 
-void alcWinMMProbe(enum DevProbe type)
+static ALCboolean ALCwinmmBackendFactory_querySupport(ALCwinmmBackendFactory* UNUSED(self), ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback || type == ALCbackend_Capture)
+        return ALC_TRUE;
+    return ALC_FALSE;
+}
+
+static void ALCwinmmBackendFactory_probe(ALCwinmmBackendFactory* UNUSED(self), enum DevProbe type)
 {
     switch(type)
     {
@@ -714,3 +774,39 @@ void alcWinMMProbe(enum DevProbe type)
             break;
     }
 }
+
+static ALCbackend* ALCwinmmBackendFactory_createBackend(ALCwinmmBackendFactory* UNUSED(self), ALCdevice *device, ALCbackend_Type type)
+{
+    if(type == ALCbackend_Playback)
+    {
+        ALCwinmmPlayback *backend;
+
+        backend = ALCwinmmPlayback_New(sizeof(*backend));
+        if(!backend) return NULL;
+        memset(backend, 0, sizeof(*backend));
+
+        ALCwinmmPlayback_Construct(backend, device);
+
+        return STATIC_CAST(ALCbackend, backend);
+    }
+    if(type == ALCbackend_Capture)
+    {
+        ALCwinmmCapture *backend;
+
+        backend = ALCwinmmCapture_New(sizeof(*backend));
+        if(!backend) return NULL;
+        memset(backend, 0, sizeof(*backend));
+
+        ALCwinmmCapture_Construct(backend, device);
+
+        return STATIC_CAST(ALCbackend, backend);
+    }
+
+    return NULL;
+}
+
+ALCbackendFactory *ALCwinmmBackendFactory_getFactory(void)
+{
+    static ALCwinmmBackendFactory factory = ALCWINMMBACKENDFACTORY_INITIALIZER;
+    return STATIC_CAST(ALCbackendFactory, &factory);
+}