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