<pre style='margin:0'>
Herby Gillot (herbygillot) pushed a commit to branch master
in repository macports-ports.

</pre>
<p><a href="https://github.com/macports/macports-ports/commit/f19d419984c33bff4d3695ccbec4b620d31f82f5">https://github.com/macports/macports-ports/commit/f19d419984c33bff4d3695ccbec4b620d31f82f5</a></p>
<pre style="white-space: pre; background: #F8F8F8"><span style='display:block; white-space:pre;color:#808000;'>commit f19d419984c33bff4d3695ccbec4b620d31f82f5
</span>Author: Sergey Fedorov <barracuda@macos-powerpc.org>
AuthorDate: Fri Nov 29 21:07:23 2024 +0800

<span style='display:block; white-space:pre;color:#404040;'>    QMPlay2: legacy version: fix YouTube search, backport bit-perfect audio
</span>---
 multimedia/QMPlay2/Portfile                        |   24 +-
 multimedia/QMPlay2/files/0007-Fix-YT-search.patch  |  414 +++++++
 .../QMPlay2/files/0008-Switch-to-yt-dlp.patch      |   85 ++
 ...llow-bit-perfect-audio-output-for-macOS-1.patch | 1207 ++++++++++++++++++++
 ...e-fetching-yt-dlp-it-does-not-work-use-Ma.patch |   61 +
 .../files/0011-YouTubeDL-move-to-QJson.patch       |   88 ++
 ...-Fix-Qt-paths.patch => 0012-Fix-Qt-paths.patch} |    0
 7 files changed, 1876 insertions(+), 3 deletions(-)

<span style='display:block; white-space:pre;color:#808080;'>diff --git a/multimedia/QMPlay2/Portfile b/multimedia/QMPlay2/Portfile
</span><span style='display:block; white-space:pre;color:#808080;'>index adca8308f22..3e2aee1897b 100644
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>--- a/multimedia/QMPlay2/Portfile
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/multimedia/QMPlay2/Portfile
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -18,6 +18,12 @@ if {${os.platform} eq "darwin" && ${os.major} < 15} {
</span>                     size    1228156
     github.tarball_from archive
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    # These are added to support YouTube:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_lib-append \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:qjson4
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    depends_run-append \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    port:yt-dlp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     patchfiles-append \
                     0001-macOS-unbreak-build-with-Qt4.patch \
                     0002-MainWidget-fix-for-missing-qt_mac_set_dock_menu.patch \
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -25,14 +31,19 @@ if {${os.platform} eq "darwin" && ${os.major} < 15} {
</span>                     0004-PlayClass-do-not-use-QRawFont.patch \
                     0005-Revert-some-broken-code.patch \
                     0006-FFMpeg-fix-compatibility-with-modern-FFMpeg.patch \
<span style='display:block; white-space:pre;background:#ffe0e0;'>-                    0007-Fix-Qt-paths.patch
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    0007-Fix-YT-search.patch \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    0008-Switch-to-yt-dlp.patch \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    0009-PortAudio-Allow-bit-perfect-audio-output-for-macOS-1.patch \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    0010-Do-not-force-fetching-yt-dlp-it-does-not-work-use-Ma.patch \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    0011-YouTubeDL-move-to-QJson.patch \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    0012-Fix-Qt-paths.patch
</span> 
     configure.args-append \
                     -DDEFAULT_QT5=OFF \
<span style='display:block; white-space:pre;background:#e0ffe0;'>+                    -DUSE_FFMPEG_VTB=OFF \
</span>                     -DUSE_JEMALLOC=OFF \
                     -DUSE_OPENGL2=OFF \
<span style='display:block; white-space:pre;background:#ffe0e0;'>-                    -DUSE_TEKSTOWO=ON \
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>-                    -DUSE_FFMPEG_VTB=OFF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                    -DUSE_TEKSTOWO=ON
</span> 
     compiler.cxx_standard   2011
 
<span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -51,6 +62,13 @@ if {${os.platform} eq "darwin" && ${os.major} < 15} {
</span>         }
     }
 
<span style='display:block; white-space:pre;background:#e0ffe0;'>+    notes "
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ${name} expects to have yt-dlp in ~/.qmplay2/ and use it for YouTube playback.\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    After installation you could make a symlink to MacPorts-provided yt-dlp:\
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    \
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    ln -s ${prefix}/bin/yt-dlp ~/.qmplay/
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+    "
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span>     variant jemalloc description "Use Jemalloc" {
         depends_lib-append \
                     port:jemalloc
<span style='display:block; white-space:pre;color:#808080;'>diff --git a/multimedia/QMPlay2/files/0007-Fix-YT-search.patch b/multimedia/QMPlay2/files/0007-Fix-YT-search.patch
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 00000000000..eb9c52917d8
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/multimedia/QMPlay2/files/0007-Fix-YT-search.patch
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,414 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From 176915efac285fe23935db666771af3b93f3bbfc Mon Sep 17 00:00:00 2001
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From: Sergey Fedorov <vital.had@gmail.com>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Date: Tue, 19 Nov 2024 15:09:39 +0800
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Subject: [PATCH 07/11] Fix YT search
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+---
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/Extensions/CMakeLists.txt |   3 +-
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/Extensions/YouTube.cpp    | 261 ++++++++++++--------------
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/Extensions/YouTube.hpp    |   7 +-
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/qmplay2/Functions.cpp             |  31 +++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/qmplay2/headers/Functions.hpp     |   2 +
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 5 files changed, 157 insertions(+), 147 deletions(-)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/Extensions/CMakeLists.txt src/modules/Extensions/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index a56ceea4..6f69bb81 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/Extensions/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/Extensions/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -117,7 +117,8 @@ add_library(${PROJECT_NAME} ${QMPLAY2_MODULE}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if(USE_QT5)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     qt5_use_modules(${PROJECT_NAME} Gui Widgets ${DBUS})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ else()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-    target_link_libraries(${PROJECT_NAME} Qt4::QtCore Qt4::QtGui ${DBUS})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    add_definitions(-I/opt/local/include/QJson4)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    target_link_libraries(${PROJECT_NAME} Qt4::QtCore Qt4::QtGui QJson4 ${DBUS})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ endif()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ add_dependencies(${PROJECT_NAME} libqmplay2)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/Extensions/YouTube.cpp src/modules/Extensions/YouTube.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 4e71605b..73924de2 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/Extensions/YouTube.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/Extensions/YouTube.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -26,6 +26,9 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QStringListModel>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QDesktopServices>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QTextDocument>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonParseError.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonObject.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonArray.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QProgressBar>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QApplication>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QHeaderView>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -819,133 +822,94 @@ void YouTube::setAutocomplete(const QByteArray &data)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   completer->complete();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-void YouTube::setSearchResults(QString data)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++void YouTube::setSearchResults(const QByteArray &data)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  /* Usuwanie komentarzy HTML */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  for (int commentIdx = 0 ;;)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const auto json = getYtInitialData(data);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const auto contents = json.object()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["contents"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["twoColumnSearchResultsRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["primaryContents"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["sectionListRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["contents"].toArray().at(0).toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["itemSectionRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["contents"].toArray()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  ;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  for (auto &&obj : contents)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if ((commentIdx = data.indexOf("<!--", commentIdx)) < 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          int commentEndIdx = data.indexOf("-->", commentIdx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if (commentEndIdx >= 0) //Jeżeli jest koniec komentarza
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  data.remove(commentIdx, commentEndIdx - commentIdx + 3); //Wyrzuć zakomentowany fragment
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  data.remove(commentIdx, data.length() - commentIdx); //Wyrzuć cały tekst do końca
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          const auto videoRenderer = obj.toObject()["videoRenderer"].toObject();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          const auto playlistRenderer = obj.toObject()["playlistRenderer"].toObject();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  int i;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  const QStringList splitted = data.split("yt-lockup ");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  for (i = 1; i < splitted.count(); ++i)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          QString title, videoInfoLink, duration, image, user;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          const QString &entry = splitted[i];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          int idx;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          const bool isVideo = !videoRenderer.isEmpty() && playlistRenderer.isEmpty();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if (entry.contains("yt-lockup-channel")) //Ignore channels
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          QString title, contentId, length, user, publishedTime, viewCount, thumbnail, url;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          const bool isPlaylist = entry.contains("yt-lockup-playlist");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if ((idx = entry.indexOf("yt-lockup-title")) > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (isVideo)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  int urlIdx = entry.indexOf("href=\"", idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  int titleIdx = entry.indexOf("title=\"", idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  if (titleIdx > -1 && urlIdx > -1 && titleIdx > urlIdx)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          const int endUrlIdx = entry.indexOf("\"", urlIdx += 6);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          const int endTitleIdx = entry.indexOf("\"", titleIdx += 7);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          if (endTitleIdx > -1 && endUrlIdx > -1 && endTitleIdx > endUrlIdx)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  videoInfoLink = entry.mid(urlIdx, endUrlIdx - urlIdx).replace("&amp;", "&");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  if (!videoInfoLink.isEmpty() && videoInfoLink.startsWith('/'))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          videoInfoLink.prepend(YOUTUBE_URL);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  title = entry.mid(titleIdx, endTitleIdx - titleIdx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  title = videoRenderer["title"].toObject()["runs"].toArray().at(0).toObject()["text"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  contentId = videoRenderer["videoId"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  if (title.isEmpty() || contentId.isEmpty())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  length = videoRenderer["lengthText"].toObject()["simpleText"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  user = videoRenderer["ownerText"].toObject()["runs"].toArray().at(0).toObject()["text"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  publishedTime = videoRenderer["publishedTimeText"].toObject()["simpleText"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  viewCount = videoRenderer["shortViewCountText"].toObject()["simpleText"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  thumbnail = videoRenderer["thumbnail"].toObject()["thumbnails"].toArray().at(0).toObject()["url"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  url = YOUTUBE_URL "/watch?v=" + contentId;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if ((idx = entry.indexOf("video-thumb")) > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  int skip = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  int imgIdx = entry.indexOf("data-thumb=\"", idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  if (imgIdx > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          skip = 12;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          imgIdx = entry.indexOf("src=\"", idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          skip = 5;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  if (imgIdx > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          int imgEndIdx = entry.indexOf("\"", imgIdx += skip);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          if (imgEndIdx > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  image = entry.mid(imgIdx, imgEndIdx - imgIdx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  if (image.endsWith(".gif")) //GIF nie jest miniaturką - jest to pojedynczy piksel :D (very old code, is it still relevant?)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          image.clear();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  else if (image.startsWith("//"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          image.prepend("https:");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  if ((idx = image.indexOf("?")) > 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          image.truncate(idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if (!isPlaylist && (idx = entry.indexOf("video-time")) > -1 && (idx = entry.indexOf(">", idx)) > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  int endIdx = entry.indexOf("<", idx += 1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  if (endIdx > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          duration = entry.mid(idx, endIdx - idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          if (!duration.startsWith("0") && duration.indexOf(":") == 1 && duration.count(":") == 1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  duration.prepend("0");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if ((idx = entry.indexOf("yt-lockup-byline")) > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  int endIdx = entry.indexOf("</a>", idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  if (endIdx > -1 && (idx = entry.lastIndexOf(">", endIdx)) > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          ++idx;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          user = entry.mid(idx, endIdx - idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  title = playlistRenderer["title"].toObject()["simpleText"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  contentId = playlistRenderer["playlistId"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  if (title.isEmpty() || contentId.isEmpty())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  user = playlistRenderer["longBylineText"].toObject()["runs"].toArray().at(0).toObject()["text"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  thumbnail = playlistRenderer
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          ["thumbnailRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          ["playlistVideoThumbnailRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          ["thumbnail"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          ["thumbnails"].toArray().at(0).toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          ["url"].toString()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  ;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  url = YOUTUBE_URL "/playlist?list=" + contentId;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if (!title.isEmpty() && !videoInfoLink.isEmpty())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  QTreeWidgetItem *tWI = new QTreeWidgetItem(resultsW);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setDisabled(true);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          auto tWI = new QTreeWidgetItem(resultsW);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  QTextDocument txtDoc;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  txtDoc.setHtml(title);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tWI->setText(0, title);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tWI->setText(1, isVideo ? length : tr("Playlist"));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tWI->setText(2, user);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setText(0, txtDoc.toPlainText());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setText(1, !isPlaylist ? duration : tr("Playlist"));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setText(2, user);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          QString tooltip;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tooltip += QString("%1: %2\n").arg(resultsW->headerItem()->text(0), tWI->text(0));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tooltip += QString("%1: %2\n").arg(isVideo ? resultsW->headerItem()->text(1) : tr("Playlist"), isVideo ? tWI->text(1) : tr("yes"));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tooltip += QString("%1: %2\n").arg(resultsW->headerItem()->text(2), tWI->text(2));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tooltip += QString("%1: %2\n").arg(tr("Published time"), publishedTime);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tooltip += QString("%1: %2").arg(tr("View count"), viewCount);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tWI->setToolTip(0, tooltip);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setToolTip(0, QString("%1: %2\n%3: %4\n%5: %6")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          .arg(resultsW->headerItem()->text(0), tWI->text(0),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          !isPlaylist ? resultsW->headerItem()->text(1) : tr("Playlist"),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          !isPlaylist ? tWI->text(1) : tr("yes"),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          resultsW->headerItem()->text(2), tWI->text(2))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  );
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tWI->setData(0, Qt::UserRole, url);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tWI->setData(1, Qt::UserRole, !isVideo);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setData(0, Qt::UserRole, videoInfoLink);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setData(1, Qt::UserRole, isPlaylist);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (!isVideo)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  tWI->setDisabled(true);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  NetworkReply *linkReply = net.start(videoInfoLink);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  NetworkReply *imageReply = net.start(image);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  linkReply->setProperty("tWI", qVariantFromValue((void *)tWI));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  imageReply->setProperty("tWI", qVariantFromValue((void *)tWI));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  auto linkReply = net.start(url);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  linkReply->setProperty("tWI", QVariant::fromValue((void *)tWI));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   linkReplies += linkReply;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (!thumbnail.isEmpty())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  auto imageReply = net.start(thumbnail);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  imageReply->setProperty("tWI", QVariant::fromValue((void *)tWI));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   imageReplies += imageReply;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  if (i == 1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          resultsW->clear();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  if (resultsW->topLevelItemCount() > 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           pageSwitcher->currPageB->setValue(currPage);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           pageSwitcher->show();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -1249,44 +1213,53 @@ QStringList YouTube::getUrlByItagPriority(const QList<int> &itags, QStringList r
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   return ret;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-void YouTube::preparePlaylist(const QString &data, QTreeWidgetItem *tWI)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++void YouTube::preparePlaylist(const QByteArray &data, QTreeWidgetItem *tWI)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  int idx = data.indexOf("playlist-videos-container");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  if (idx > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  QStringList playlist;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const auto json = getYtInitialData(data);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const auto contents = json.object()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["contents"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["twoColumnBrowseResultsRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["tabs"].toArray().at(0).toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["tabRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["content"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["sectionListRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["contents"].toArray().at(0).toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["itemSectionRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["contents"].toArray().at(0).toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["playlistVideoListRenderer"].toObject()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          ["contents"].toArray()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  ;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  for (auto &&obj : contents)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          const QString tags[2] = {"video-id", "video-title"};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          QStringList playlist, entries = data.mid(idx).split("yt-uix-scroller-scroll-unit", QString::SkipEmptyParts);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          entries.removeFirst();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          for (const QString &entry : entries)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  QStringList plistEntry;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  for (int i = 0; i < 2; ++i)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          idx = entry.indexOf(tags[i]);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          if (idx > -1 && (idx = entry.indexOf('"', idx += tags[i].length())) > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  const int endIdx = entry.indexOf('"', idx += 1);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  if (endIdx > -1)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          const QString str = entry.mid(idx, endIdx - idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          if (!i)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  plistEntry += str;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  QTextDocument txtDoc;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  txtDoc.setHtml(str);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  plistEntry += txtDoc.toPlainText();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  if (plistEntry.count() == 2)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                          playlist += plistEntry;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if (!playlist.isEmpty())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setData(0, Qt::UserRole + 1, playlist);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  tWI->setDisabled(false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          const auto playlistRenderer = obj.toObject()["playlistVideoRenderer"].toObject();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          const auto title = playlistRenderer["title"].toObject()["simpleText"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          const auto videoId = playlistRenderer["videoId"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (title.isEmpty() || videoId.isEmpty())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          playlist += {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  videoId,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  title,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  if (!playlist.isEmpty())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tWI->setData(0, Qt::UserRole + 1, playlist);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          tWI->setDisabled(false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++QJsonDocument YouTube::getYtInitialData(const QByteArray &data)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  int idx = data.indexOf("ytInitialData");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  if (idx < 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          return QJsonDocument();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  idx = data.indexOf("{", idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  if (idx < 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          return QJsonDocument();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  int idx2 = Functions::findJsonEnd(data, idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  if (idx2 < 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          return QJsonDocument();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const auto jsonData = data.mid(idx, idx2 - idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  return QJsonDocument::fromJson(jsonData);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/Extensions/YouTube.hpp src/modules/Extensions/YouTube.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 97e7be21..9070b408 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/Extensions/YouTube.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/Extensions/YouTube.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -25,6 +25,7 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QTreeWidget>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QPointer>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QMap>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonDocument.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ class QProgressBar;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ class QToolButton;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -132,13 +133,15 @@ private:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   void deleteReplies();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   void setAutocomplete(const QByteArray &data);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  void setSearchResults(QString data);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  void setSearchResults(const QByteArray &data);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   QStringList getYouTubeVideo(const QString &data, const QString &PARAM = QString(), QTreeWidgetItem *tWI = nullptr, const QString &url = QString(), IOController<YouTubeDL> *youtube_dl = nullptr); //jeżeli (tWI == nullptr) to zwraca {URL, file_extension, TITLE}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   QStringList getUrlByItagPriority(const QList<int> &itags, QStringList ret);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  void preparePlaylist(const QString &data, QTreeWidgetItem *tWI);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  void preparePlaylist(const QByteArray &data, QTreeWidgetItem *tWI);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  QJsonDocument getYtInitialData(const QByteArray &data);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++private:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   DockWidget *dw;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   QIcon youtubeIcon, videoIcon;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/qmplay2/Functions.cpp src/qmplay2/Functions.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 35e59931..03ec70ee 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/qmplay2/Functions.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/qmplay2/Functions.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -912,3 +912,34 @@ QByteArray Functions::decryptAes256Cbc(const QByteArray &password, const QByteAr
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   deciphered.resize(decryptedLen + finalizeLen);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   return deciphered;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++int Functions::findJsonEnd(const QByteArray &data, int idx)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const int dataLen = data.length();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  if (dataLen < 1 || idx < 0 || idx >= dataLen || data.at(idx) != '{')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          return -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  int brackets = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  bool inString = false;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  char prevChr = '\0';
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  for (int i = idx + 1; i < dataLen; ++i)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          const char chr = data.at(i);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (chr == '"')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  if (!inString)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          inString = true;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  else if (prevChr != '\\')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          inString = false;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          prevChr = chr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (inString)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  continue;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (chr == '{')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  ++brackets;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          else if (chr == '}')
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  --brackets;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (brackets == 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  return i + 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  return -1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/qmplay2/headers/Functions.hpp src/qmplay2/headers/Functions.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index e74c48f1..ef4dd090 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/qmplay2/headers/Functions.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/qmplay2/headers/Functions.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -158,4 +158,6 @@ namespace Functions
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   void setHeaderSectionResizeMode(QHeaderView *header, int index, int resizeMode);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   QByteArray decryptAes256Cbc(const QByteArray &password, const QByteArray &salt, const QByteArray &ciphered);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  int findJsonEnd(const QByteArray &data, int idx = 0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/multimedia/QMPlay2/files/0008-Switch-to-yt-dlp.patch b/multimedia/QMPlay2/files/0008-Switch-to-yt-dlp.patch
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 00000000000..fc70aafd8c1
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/multimedia/QMPlay2/files/0008-Switch-to-yt-dlp.patch
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,85 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From bf4f953cf6f083d6bc876543f7ac8e77a123abbf Mon Sep 17 00:00:00 2001
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From: Sergey Fedorov <vital.had@gmail.com>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Date: Tue, 19 Nov 2024 17:04:56 +0800
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Subject: [PATCH 08/11] Switch to yt-dlp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+---
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/qmplay2/YouTubeDL.cpp | 40 ++++++++++-----------------------------
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 1 file changed, 10 insertions(+), 30 deletions(-)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/qmplay2/YouTubeDL.cpp src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index de7b96be..98071fea 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -22,10 +22,9 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QMPlay2Core.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <Version.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <Json11.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-#ifdef Q_OS_WIN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  #include <Functions.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <Functions.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QRegExp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QReadWriteLock>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QFile>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -50,9 +49,9 @@ static void exportCookiesFromJSON(const QString &jsonData, const QString &url)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ QString YouTubeDL::getFilePath()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  return QMPlay2Core.getSettingsDir() + "youtube-dl"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  return QMPlay2Core.getSettingsDir() + "yt-dlp"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #ifdef Q_OS_WIN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  ".exe"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  "_x86.exe"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   ;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -286,30 +285,11 @@ QStringList YouTubeDL::exec(const QString &url, const QStringList &args, QString
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   updateOutput += m_process.readAllStandardOutput() + m_process.readAllStandardError();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   if (updateOutput.contains("ERROR:") || updateOutput.contains("package manager"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           error += "\n" + updateOutput;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  else if (m_process.exitCode() == 0 && !updateOutput.contains("up-to-date"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                  else if (m_process.exitCode() == 0 && !updateOutput.contains(QRegExp(R"(up\Wto\Wdate)")))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-#ifdef Q_OS_WIN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          const QString updatedFile = ytDlPath + ".new";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          QFile::remove(Functions::filePath(ytDlPath) + "youtube-dl-updater.bat");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          if (QFile::exists(updatedFile))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  Functions::s_wait(0.2); //Wait 200 ms to be sure that file is closed
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  QFile::remove(ytDlPath);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  if (QFile::rename(updatedFile, ytDlPath))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                          QMPlay2Core.setWorking(false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                          emit QMPlay2Core.sendMessage(tr("\"youtube-dl\" has been successfully updated!"), g_name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                          g_lock.unlock(); // Unlock for write
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                          return exec(url, args, silentErr, false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-#ifdef Q_OS_WIN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                                  error += "\nUpdated youtube-dl file: \"" + updatedFile + "\" not found!";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                          QMPlay2Core.setWorking(false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                          emit QMPlay2Core.sendMessage(tr("\"youtube-dl\" has been successfully updated!"), g_name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                          return {};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           else if (updating && m_aborted)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -339,9 +319,9 @@ QStringList YouTubeDL::exec(const QString &url, const QStringList &args, QString
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   else if (canUpdate && !m_aborted && m_process.error() == QProcess::FailedToStart)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          const QString downloadUrl = "https://yt-dl.org/downloads/latest/youtube-dl"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          const QString downloadUrl = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #ifdef Q_OS_WIN
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          ".exe"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          "_x86.exe"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           ;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/multimedia/QMPlay2/files/0009-PortAudio-Allow-bit-perfect-audio-output-for-macOS-1.patch b/multimedia/QMPlay2/files/0009-PortAudio-Allow-bit-perfect-audio-output-for-macOS-1.patch
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 00000000000..6ec8eb1db4d
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/multimedia/QMPlay2/files/0009-PortAudio-Allow-bit-perfect-audio-output-for-macOS-1.patch
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,1207 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From 4ce1e7e12d4801d03c25ae6b9ef12ef366c0b083 Mon Sep 17 00:00:00 2001
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From: =?UTF-8?q?Ren=C3=A9=20Bertin?= <rjvbertin@gmail.com>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Date: Mon, 12 Feb 2018 18:20:07 +0100
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Subject: [PATCH] PortAudio: Allow bit-perfect audio output for macOS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ (#108)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+When QMPlay2 isn't configured to resample all audio content to a
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+user-specific sample rate it sends the demuxed and decoded audio stream
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+"as is" to the selected audio device. When using the PortAudio module a
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+stream is opened that is set to the content sample rate, but no attempt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+is made to configure the device appropriately.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+This patch introduces an initial implementation of a bit-perfect audio
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+option in the PortAudio module for macOS. After opening the PA stream,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+platform-native APIs are called to switch the selected audio device to
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+the content's sample rate or to the most appropriate (closest) supported
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+sample rate. The initial device settings are cached and restored when
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+playback is stopped.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+barracuda156: Some fixes added for Qt4 compat.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+---
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ .../3rdparty/CoreAudio/AudioDevice.h          | 136 ++++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ .../3rdparty/CoreAudio/AudioDevice.mm         | 645 ++++++++++++++++++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ .../3rdparty/CoreAudio/AudioDeviceList.cpp    |  98 +++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ .../3rdparty/CoreAudio/AudioDeviceList.h      |  82 +++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/PortAudio/CMakeLists.txt          |  10 +
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/PortAudio/PortAudio.cpp           |  12 +
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/PortAudio/PortAudio.hpp           |   3 +
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/PortAudio/PortAudioWriter.cpp     |  53 +-
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/PortAudio/PortAudioWriter.hpp     |   7 +
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 9 files changed, 1045 insertions(+), 1 deletion(-)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ create mode 100644 src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ create mode 100644 src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.mm
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ create mode 100644 src/modules/PortAudio/3rdparty/CoreAudio/AudioDeviceList.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ create mode 100644 src/modules/PortAudio/3rdparty/CoreAudio/AudioDeviceList.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.h src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+new file mode 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 00000000..9d5788e4
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -0,0 +1,136 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++     File: AudioDevice.h 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Adapted from the CAPlayThough example
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  Version: 1.2.2 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Inc. ("Apple") in consideration of your agreement to the following
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ terms, and your use, installation, modification or redistribution of
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ this Apple software constitutes acceptance of these terms.  If you do
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ not agree with these terms, please do not use, install, modify or
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ redistribute this Apple software. 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ In consideration of your agreement to abide by the following terms, and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ subject to these terms, Apple grants you a personal, non-exclusive
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ license, under Apple's copyrights in this original Apple software (the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ "Apple Software"), to use, reproduce, modify and redistribute the Apple
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Software, with or without modifications, in source and/or binary forms;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ provided that if you redistribute the Apple Software in its entirety and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ without modifications, you must retain this notice and the following
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ text and disclaimers in all such redistributions of the Apple Software.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Neither the name, trademarks, service marks or logos of Apple Inc. may
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ be used to endorse or promote products derived from the Apple Software
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ without specific prior written permission from Apple.  Except as
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ expressly stated in this notice, no other rights or licenses, express or
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ implied, are granted by Apple herein, including but not limited to any
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ patent rights that may be infringed by your derivative works or by other
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ works in which the Apple Software may be incorporated.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Copyright (C) 2013 Apple Inc. All Rights Reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Copyright (C) 2017,18 René J.V. Bertin All Rights Reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++*/
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifndef __AudioDevice_h__
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#define __AudioDevice_h__
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <CoreServices/CoreServices.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <CoreAudio/CoreAudio.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <AvailabilityMacros.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <qglobal.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifndef DEPRECATED_LISTENER_API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++# if MAC_OS_X_VERSION_MIN_REQUIRED < 101100
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#         define DEPRECATED_LISTENER_API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#         warning "Using the deprecated PropertyListener API; at least it works"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++# endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef DEPRECATED_LISTENER_API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++using AudioPropertyListenerProc = AudioDevicePropertyListenerProc;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++using AudioPropertyListenerProc = AudioObjectPropertyListenerProc;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++class AudioDeviceList;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++class AudioDevice {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++public:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioDevice();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioDevice(AudioDeviceID devid, bool isInput=false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioDevice(AudioDeviceID devid, AudioPropertyListenerProc lProc, bool isInput=false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  ~AudioDevice();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  void Init(AudioPropertyListenerProc lProc);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  bool Valid() { return mID != kAudioDeviceUnknown; }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  void SetBufferSize(UInt32 size);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  OSStatus NominalSampleRate(Float64 &sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  inline Float64 ClosestNominalSampleRate(Float64 sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  OSStatus SetNominalSampleRate(Float64 sampleRate, Boolean force=false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  OSStatus ResetNominalSampleRate(Boolean force=false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  OSStatus SetStreamBasicDescription(AudioStreamBasicDescription *desc);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  int CountChannels();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  char *GetName(char *buf=NULL, UInt32 maxlen=0);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  void SetInitialNominalSampleRate(Float64 sampleRate)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          mInitialFormat.mSampleRate = sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  Float64 CurrentNominalSampleRate()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          return currentNominalSR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioDeviceID ID()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          return mID;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  static AudioDevice *GetDefaultDevice(Boolean forInput, OSStatus &err, AudioDevice *dev=NULL);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  static AudioDevice *GetDevice(AudioDeviceID devId, Boolean forInput, AudioDevice *dev=NULL);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++protected:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioDevice(AudioDeviceID devid, bool quick, bool isInput);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioStreamBasicDescription mInitialFormat;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioPropertyListenerProc listenerProc;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  OSStatus GetPropertyDataSize( AudioObjectPropertySelector property, UInt32 *size, AudioObjectPropertyAddress *propertyAddress=NULL );
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  Float64 currentNominalSR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  Float64 minNominalSR, maxNominalSR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  UInt32 nominalSampleRates;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  Float64 *nominalSampleRateList = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  bool discreteSampleRateList;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const AudioDeviceID mID;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const bool mForInput;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  UInt32 mSafetyOffset;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  UInt32 mBufferSizeFrames;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioStreamBasicDescription mFormat;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  char mDevName[256] = "";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  bool mInitialised = false;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++friend class AudioDeviceList;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++public:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  UInt32 listenerSilentFor;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif // __AudioDevice_h__
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.mm src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.mm
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+new file mode 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 00000000..f631b4e7
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/3rdparty/CoreAudio/AudioDevice.mm
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -0,0 +1,645 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++     File: AudioDevice.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Adapted from the CAPlayThough example
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  Version: 1.2.2
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Inc. ("Apple") in consideration of your agreement to the following
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ terms, and your use, installation, modification or redistribution of
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ this Apple software constitutes acceptance of these terms.  If you do
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ not agree with these terms, please do not use, install, modify or
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ redistribute this Apple software.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ In consideration of your agreement to abide by the following terms, and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ subject to these terms, Apple grants you a personal, non-exclusive
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ license, under Apple's copyrights in this original Apple software (the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ "Apple Software"), to use, reproduce, modify and redistribute the Apple
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Software, with or without modifications, in source and/or binary forms;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ provided that if you redistribute the Apple Software in its entirety and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ without modifications, you must retain this notice and the following
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ text and disclaimers in all such redistributions of the Apple Software.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Neither the name, trademarks, service marks or logos of Apple Inc. may
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ be used to endorse or promote products derived from the Apple Software
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ without specific prior written permission from Apple.  Except as
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ expressly stated in this notice, no other rights or licenses, express or
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ implied, are granted by Apple herein, including but not limited to any
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ patent rights that may be infringed by your derivative works or by other
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ works in which the Apple Software may be incorporated.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Copyright (C) 2013 Apple Inc. All Rights Reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Copyright (C) 2017,18 René J.V. Bertin All Rights Reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++*/
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include "AudioDevice.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#import <Cocoa/Cocoa.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    #define verify_noerr __Verify_noErr
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++char *OSTStr(OSType type)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    static union OSTStr {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        uint32_t four;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        char str[5];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    } ltype;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    ltype.four = EndianU32_BtoN(type);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    ltype.str[4] = '\0';
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return ltype.str;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++// the sample rates that can be found in the selections proposed by Audio Midi Setup. Are these representative
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++// for the devices I have at my disposal, or are they determined by discrete supported values hardcoded into
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++// CoreAudio or the HAL? Is there any advantage in using one of these rates, as opposed to using a different rate
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++// on devices that support any rate in an interval?
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++static Float64 supportedSRateList[] = {6400, 8000, 11025, 12000, 16000, 22050,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                       24000, 32000, 44100, 48000, 64000, 88200, 96000, 128000, 176400, 192000
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                      };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++static UInt32 supportedSRates = sizeof(supportedSRateList) / sizeof(Float64);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef DEPRECATED_LISTENER_API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++OSStatus DefaultListener(AudioDeviceID inDevice, UInt32 inChannel, Boolean forInput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                         AudioDevicePropertyID inPropertyID,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                         void *inClientData)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 size;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    Float64 sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioDevice *dev = (AudioDevice *) inClientData;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    NSString *msg = [NSString stringWithFormat:@"Property %s of device %u changed; data=%p",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                     OSTStr((OSType)inPropertyID), (unsigned int)inDevice, inClientData ];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress theAddress = { inPropertyID,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              forInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                            };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    switch (inPropertyID) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        case kAudioDevicePropertyNominalSampleRate:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            size = sizeof(sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            if (AudioObjectGetPropertyData(inPropertyID, &theAddress, 0, NULL, &size, &sampleRate) == noErr
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    && (dev && !dev->listenerSilentFor)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                NSLog(@"%@\n\tkAudioDevicePropertyNominalSampleRate=%g\n", msg, sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        case kAudioDevicePropertyActualSampleRate:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            size = sizeof(sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            if (AudioObjectGetPropertyData(inPropertyID, &theAddress, 0, NULL, &size, &sampleRate) == noErr && dev) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                // update the rate we should reset to
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                dev->SetInitialNominalSampleRate(sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                if (!dev->listenerSilentFor) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    NSLog(@"%@\n\tkAudioDevicePropertyActualSampleRate=%g\n", msg, sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        default:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            if ((dev && !dev->listenerSilentFor)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                NSLog(@"%@", msg);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (dev && dev->listenerSilentFor) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        dev->listenerSilentFor -= 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (pool) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        [pool drain];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return noErr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#else   // !DEPRECATED_LISTENER_API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++static OSStatus DefaultListener(AudioObjectID inObjectID, UInt32 inNumberProperties,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                const AudioObjectPropertyAddress propTable[],
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                void *inClientData)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 size;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    Float64 sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioDevice *dev = static_cast<AudioDevice *>(inClientData);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    for (int i = 0 ; i < inNumberProperties ; ++i) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        NSString *msg = [NSString stringWithFormat:@"#%d Property %s of device %u changed; data=%p", i,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                         OSTStr((OSType)propTable[i].mElement), (unsigned int)inObjectID, inClientData ];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        switch (propTable[i].mElement) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            case kAudioDevicePropertyNominalSampleRate:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                size = sizeof(sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                if (AudioObjectGetPropertyData(inObjectID, &propTable[i], 0, NULL, &size, &sampleRate) == noErr
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        && (dev && !dev->listenerSilentFor)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                   ) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    NSLog(@"%@\n\tkAudioDevicePropertyNominalSampleRate=%g\n", msg, sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            case kAudioDevicePropertyActualSampleRate:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                size = sizeof(sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                if (AudioObjectGetPropertyData(inObjectID, &propTable[i], 0, NULL, &size, &sampleRate) == noErr && dev) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    // update the rate we should reset to
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    dev->SetInitialNominalSampleRate(sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    if (!dev->listenerSilentFor) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        NSLog(@"%@\n\tkAudioDevicePropertyActualSampleRate=%g\n", msg, sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            default:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                if ((dev && !dev->listenerSilentFor)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    NSLog(@"%@", msg);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                break;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (dev && dev->listenerSilentFor) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        dev->listenerSilentFor -= 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (pool) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        [pool drain];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return noErr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++void AudioDevice::Init(AudioPropertyListenerProc lProc = DefaultListener)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (mID == kAudioDeviceUnknown) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        return;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    OSStatus err = noErr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    // getting the device name can be surprisingly slow, so we get and cache it here
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    GetName();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 propsize = sizeof(Float32);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress theAddress = { kAudioDevicePropertySafetyOffset,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              mForInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                            }; // channel
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    verify_noerr(AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &propsize, &mSafetyOffset));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    propsize = sizeof(UInt32);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    theAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    verify_noerr(AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &propsize, &mBufferSizeFrames));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    listenerProc = lProc;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    listenerSilentFor = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (lProc) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef DEPRECATED_LISTENER_API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        AudioDeviceAddPropertyListener(mID, 0, false, kAudioDevicePropertyActualSampleRate, lProc, this);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        AudioDeviceAddPropertyListener(mID, 0, false, kAudioDevicePropertyNominalSampleRate, lProc, this);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        AudioDeviceAddPropertyListener(mID, 0, false, kAudioHardwarePropertyDefaultOutputDevice, lProc, this);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        AudioObjectPropertyAddress prop = { kAudioDevicePropertyActualSampleRate,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                            kAudioObjectPropertyScopeGlobal,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                            kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                          };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if ((err = AudioObjectAddPropertyListener(mID, &prop, lProc, this)) != noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            NSLog(@"Couldn't register property listener for actual sample rate: %d (%s)", err, OSTStr(err));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        prop.mElement = kAudioDevicePropertyNominalSampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if ((err = AudioObjectAddPropertyListener(mID, &prop, lProc, this)) != noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            NSLog(@"Couldn't register property listener for nominal sample rate: %d (%s)", err, OSTStr(err));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        prop.mElement = kAudioHardwarePropertyDefaultOutputDevice;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if ((err = AudioObjectAddPropertyListener(mID, &prop, lProc, this)) != noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            NSLog(@"Couldn't register property listener for selected default device: %d (%s)", err, OSTStr(err));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        NSLog(@"Warning: no CoreAudio event listener has been defined");
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    propsize = sizeof(Float64);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    theAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    verify_noerr(AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &propsize, &currentNominalSR));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    propsize = sizeof(AudioStreamBasicDescription);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    theAddress.mSelector = kAudioDevicePropertyStreamFormat;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    verify_noerr(AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &propsize, &mInitialFormat));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    mFormat = mInitialFormat;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    propsize = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    theAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    // attempt to build a list of the supported sample rates
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if ((err = AudioObjectGetPropertyDataSize(mID, &theAddress, 0, NULL, &propsize)) == noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        AudioValueRange *list;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        // use a fall-back value of 100 supported rates:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (propsize == 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            propsize = 100 * sizeof(AudioValueRange);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if ((list = (AudioValueRange *) calloc(1, propsize))) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            theAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            err = AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &propsize, list);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            if (err == noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                UInt32 i;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                nominalSampleRates = propsize / sizeof(AudioValueRange);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                NSMutableArray *a = [NSMutableArray arrayWithCapacity:nominalSampleRates];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                minNominalSR = list[0].mMinimum;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                maxNominalSR = list[0].mMaximum;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                // store the returned sample rates in [a] and record the extreme values
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                for (i = 0 ; i < nominalSampleRates ; i++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    if (minNominalSR > list[i].mMinimum) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        minNominalSR = list[i].mMinimum;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    if (maxNominalSR < list[i].mMaximum) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        maxNominalSR = list[i].mMaximum;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    if (a) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        if (list[i].mMinimum != list[i].mMaximum) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            UInt32 j;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            discreteSampleRateList = false;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // the 'guessing' case: the device specifies one or more ranges, without
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // indicating which rates in that range(s) are supported. We assume the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // rates that Audio Midi Setup shows.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            for (j = 0 ; j < supportedSRates ; j++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                if (supportedSRateList[j] >= list[i].mMinimum
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                        && supportedSRateList[j] <= list[i].mMaximum
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                   ) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                    [a addObject:[NSNumber numberWithDouble:supportedSRateList[j]]];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // there's at least one part of the sample rate list that contains discrete
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // supported values. I don't know if there are devices that "do this" or if they all
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // either give discrete rates or a single continuous range. So we take the easy
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // opt-out solution: this only costs a few cycles attempting to match a requested
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // non-listed rate (with the potential "risk" of matching to a listed integer multiple,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // which should not cause any aliasing).
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            discreteSampleRateList = true;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            // the easy case: the device specifies one or more discrete rates
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            if (![a containsObject:[NSNumber numberWithDouble:list[i].mMinimum]]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                [a addObject:[NSNumber numberWithDouble:list[i].mMinimum]];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                if (a) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    // sort the array (should be the case but one never knows)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    [a sortUsingSelector:@selector(compare:)];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    // retrieve the number of unique rates:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    nominalSampleRates = [a count];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    // now copy the rates into a simple C array for faster access
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    if ((nominalSampleRateList = new Float64[nominalSampleRates])) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        for (i = 0 ; i < nominalSampleRates ; i++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                            nominalSampleRateList[i] = [[a objectAtIndex:i] doubleValue];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    NSLog(@"Using audio device %u \"%s\", %u sample rates in %lu range(s); [%g,%g] %@; current sample rate %gHz",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          mID, GetName(), nominalSampleRates, propsize / sizeof(AudioValueRange),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          minNominalSR, maxNominalSR, (discreteSampleRateList) ? [a description] : @"continuous", currentNominalSR);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    NSLog(@"Using audio device %u \"%s\", %u sample rates in %lu range(s); [%g,%g] %s; current sample rate %gHz",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          mID, GetName(), nominalSampleRates, propsize / sizeof(AudioValueRange),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          minNominalSR, maxNominalSR, (discreteSampleRateList) ? "" : "continuous", currentNominalSR);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                // [a] will be flushed down the drain:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                [pool drain];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            free(list);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        mInitialised = true;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDevice::AudioDevice()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    : mID(kAudioDeviceUnknown)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    , mForInput(false)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    listenerProc = NULL;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDevice::AudioDevice(AudioDeviceID devid, bool forInput)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    : mID(devid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    , mForInput(forInput)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    Init(DefaultListener);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDevice::AudioDevice(AudioDeviceID devid, bool quick, bool forInput)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    : mID(devid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    , mForInput(forInput)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (!quick) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        Init(DefaultListener);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDevice::AudioDevice(AudioDeviceID devid, AudioPropertyListenerProc lProc, bool forInput)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    : mID(devid)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    , mForInput(forInput)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    Init(lProc);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDevice::~AudioDevice()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (mID != kAudioDeviceUnknown && mInitialised) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        OSStatus err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        AudioDeviceID devId = mID;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        // RJVB 20120902: setting the StreamFormat to the initially read values will set the channel bitdepth to 16??
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        // so we reset just the nominal sample rate.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        err = SetNominalSampleRate(mInitialFormat.mSampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (err != noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            fprintf(stderr, "Cannot reset initial settings for device %u (%s): err %s, %ld\n",
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    (unsigned int) mID, GetName(), OSTStr(err), (long) err);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (listenerProc) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef DEPRECATED_LISTENER_API
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            AudioDeviceRemovePropertyListener(mID, 0, false, kAudioDevicePropertyActualSampleRate, listenerProc);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            AudioDeviceRemovePropertyListener(mID, 0, false, kAudioDevicePropertyNominalSampleRate, listenerProc);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            AudioDeviceRemovePropertyListener(mID, 0, false, kAudioHardwarePropertyDefaultOutputDevice, listenerProc);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            AudioObjectPropertyAddress prop = { kAudioDevicePropertyActualSampleRate,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                kAudioObjectPropertyScopeGlobal,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            verify_noerr(AudioObjectRemovePropertyListener(mID, &prop, listenerProc, this));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            prop.mElement = kAudioDevicePropertyNominalSampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            verify_noerr(AudioObjectRemovePropertyListener(mID, &prop, listenerProc, this));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            prop.mElement = kAudioHardwarePropertyDefaultOutputDevice;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            verify_noerr(AudioObjectRemovePropertyListener(mID, &prop, listenerProc, this));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (nominalSampleRateList) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            delete nominalSampleRateList;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        NSLog(@"AudioDevice %s (%u) released", mDevName, devId);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++void AudioDevice::SetBufferSize(UInt32 size)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 propsize = sizeof(UInt32);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyBufferFrameSize,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              mForInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                            }; // channel
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    verify_noerr(AudioObjectSetPropertyData(mID, &theAddress, 0, NULL, propsize, &size));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    verify_noerr(AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &propsize, &mBufferSizeFrames));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++OSStatus AudioDevice::NominalSampleRate(Float64 &sampleRate)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 size = sizeof(Float64);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    OSStatus err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyNominalSampleRate,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              mForInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                            };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    err = AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &size, &sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (err == noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        currentNominalSR = sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++inline Float64 AudioDevice::ClosestNominalSampleRate(Float64 sampleRate)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (sampleRate > 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifndef FORCE_STANDARD_SAMPLERATES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (!discreteSampleRateList && sampleRate >= minNominalSR && sampleRate <= maxNominalSR) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            // the device suggests it supports this exact sample rate; use it.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            listenerSilentFor = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            return sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (nominalSampleRateList && sampleRate >= minNominalSR && sampleRate <= maxNominalSR) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            Float64 minRemainder = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            Float64 closest = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            for (UInt32 i = 0 ; i < nominalSampleRates ; i++) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                // check if we have a hit:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                if (sampleRate == nominalSampleRateList[i]) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    return sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                double dec, ent;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                dec = modf(nominalSampleRateList[i] / sampleRate, &ent);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                // if the rate at i is an integer multiple of the requested sample rate.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                if (dec == 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    listenerSilentFor = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    return nominalSampleRateList[i];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                } else if ((1 - dec) < minRemainder) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    // find the match that is closest to either 1*sampleRate or 2*sampleRate
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    // IOW, the fractional part of nominalSampleRate/sampleRate is closest
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    // either to 0 or to 1 .
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    minRemainder = dec;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    closest = nominalSampleRateList[i];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            if (closest > 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                listenerSilentFor = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                return closest;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        static bool pass2 = false;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (!pass2) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            Float64 sr = sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            int fact = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            // if we're here it's either because there's no list of known supported rates,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            // or we didn't find an integer multiple of the requested rate in the list.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            // scale up as required in steps of 2
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            while (sampleRate * fact < minNominalSR && sampleRate * (fact + 1) <= maxNominalSR) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                fact += 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            sampleRate *= fact;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            fact = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            // scale down as required in steps of 2
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            while (sampleRate / fact > maxNominalSR && sampleRate / (fact + 1) >= minNominalSR) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                fact += 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            sampleRate /= fact;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            if (sr != sampleRate) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                pass2 = true;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                // we're now in range, do another pass to find a matching supported rate
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                sampleRate = ClosestNominalSampleRate(sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                pass2 = false;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                listenerSilentFor = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                if (sampleRate > maxNominalSR) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    sampleRate = maxNominalSR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                    sampleRate = minNominalSR;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            // note that we really ought to resample the content if we're sending it to a 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            // device running at a lower sample rate!
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++OSStatus AudioDevice::SetNominalSampleRate(Float64 sampleRate, Boolean force)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 size = sizeof(Float64);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    OSStatus err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (sampleRate <= 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        return paramErr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    listenerSilentFor = 2;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    Float64 sampleRate2 = ClosestNominalSampleRate(sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    NSLog(@"SetNominalSampleRate(%g) setting rate to %gHz", sampleRate, sampleRate2);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (sampleRate2 != currentNominalSR || force) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyNominalSampleRate,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                  mForInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                  kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        err = AudioObjectSetPropertyData(mID, &theAddress, 0, NULL, size, &sampleRate2);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (err == noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            currentNominalSR = sampleRate2;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            NSLog(@"Failure setting device \"%s\" to %gHz: %d (%s)", GetName(), sampleRate2, err, OSTStr(err));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        err = noErr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++/*!
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    Reset the nominal sample rate to the value found when opening the device
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++OSStatus AudioDevice::ResetNominalSampleRate(Boolean force)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 size = sizeof(Float64);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    Float64 sampleRate = mInitialFormat.mSampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    OSStatus err = noErr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (sampleRate != currentNominalSR || force) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        listenerSilentFor = 2;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyNominalSampleRate,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                  mForInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                  kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        err = AudioObjectSetPropertyData(mID, &theAddress, 0, NULL, size, &sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (err == noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            currentNominalSR = sampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++OSStatus AudioDevice::SetStreamBasicDescription(AudioStreamBasicDescription *desc)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 size = sizeof(AudioStreamBasicDescription);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    OSStatus err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    listenerSilentFor = 1;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyStreamFormat,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              mForInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                            };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    err = AudioObjectSetPropertyData(mID, &theAddress, 0, NULL, size, desc);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (err == noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        currentNominalSR = desc->mSampleRate;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++// AudioDeviceGetPropertyInfo() is deprecated, so we wrap AudioObjectGetPropertyDataSize().
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++OSStatus AudioDevice::GetPropertyDataSize(AudioObjectPropertySelector property, UInt32 *size, AudioObjectPropertyAddress *propertyAddress)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress l_propertyAddress;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (!propertyAddress) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        propertyAddress = &l_propertyAddress;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    propertyAddress->mSelector = property;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    propertyAddress->mScope = (mForInput) ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    propertyAddress->mElement = kAudioObjectPropertyElementMaster;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return AudioObjectGetPropertyDataSize(mID, propertyAddress, 0, NULL, size);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++int AudioDevice::CountChannels()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    OSStatus err;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 propSize;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress theAddress;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    int result = 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    err = GetPropertyDataSize(kAudioDevicePropertyStreamConfiguration, &propSize, &theAddress);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (err) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        return 0;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioBufferList *buflist = (AudioBufferList *)malloc(propSize);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    err = AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &propSize, buflist);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (!err) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        for (UInt32 i = 0; i < buflist->mNumberBuffers; ++i) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            result += buflist->mBuffers[i].mNumberChannels;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    free(buflist);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++char *AudioDevice::GetName(char *buf, UInt32 maxlen)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (!buf) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        buf = mDevName;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        maxlen = sizeof(mDevName) / sizeof(char);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (*buf) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            return buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyDeviceName,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              mForInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                              kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                            }; // channel
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    verify_noerr(AudioObjectGetPropertyData(mID, &theAddress, 0, NULL, &maxlen, buf));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return buf;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDevice *AudioDevice::GetDefaultDevice(Boolean forInput, OSStatus &err, AudioDevice *dev)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    UInt32 propsize;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioDeviceID defaultDeviceID;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    propsize = sizeof(AudioDeviceID);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    AudioObjectPropertyAddress theAddress = {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        forInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        kAudioObjectPropertyScopeGlobal,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    err = AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, &defaultDeviceID);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (err == noErr) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (dev) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            if (dev->ID() != defaultDeviceID) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                delete dev;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                goto bail;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        dev = new AudioDevice(defaultDeviceID, forInput);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++bail:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    ;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return dev;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDevice *AudioDevice::GetDevice(AudioDeviceID devId, Boolean forInput, AudioDevice *dev)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if (dev) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        if (dev->ID() != devId) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            delete dev;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        } else {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++            goto bail;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    dev = new AudioDevice(devId, forInput);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++bail:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    ;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    return dev;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++// kate: indent-mode cstyle; indent-width 4; replace-tabs on;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/3rdparty/CoreAudio/AudioDeviceList.cpp src/modules/PortAudio/3rdparty/CoreAudio/AudioDeviceList.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+new file mode 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 00000000..ba3e00f1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/3rdparty/CoreAudio/AudioDeviceList.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -0,0 +1,98 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++     File: AudioDeviceList.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Adapted from the CAPlayThough example
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  Version: 1.2.2
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Inc. ("Apple") in consideration of your agreement to the following
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ terms, and your use, installation, modification or redistribution of
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ this Apple software constitutes acceptance of these terms.  If you do
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ not agree with these terms, please do not use, install, modify or
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ redistribute this Apple software.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ In consideration of your agreement to abide by the following terms, and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ subject to these terms, Apple grants you a personal, non-exclusive
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ license, under Apple's copyrights in this original Apple software (the
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ "Apple Software"), to use, reproduce, modify and redistribute the Apple
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Software, with or without modifications, in source and/or binary forms;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ provided that if you redistribute the Apple Software in its entirety and
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ without modifications, you must retain this notice and the following
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ text and disclaimers in all such redistributions of the Apple Software.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Neither the name, trademarks, service marks or logos of Apple Inc. may
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ be used to endorse or promote products derived from the Apple Software
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ without specific prior written permission from Apple.  Except as
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ expressly stated in this notice, no other rights or licenses, express or
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ implied, are granted by Apple herein, including but not limited to any
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ patent rights that may be infringed by your derivative works or by other
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ works in which the Apple Software may be incorporated.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ The Apple Software is provided by Apple on an "AS IS" basis.  APPLE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ POSSIBILITY OF SUCH DAMAGE.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Copyright (C) 2013 Apple Inc. All Rights Reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Copyright (C) 2017,18 René J.V. Bertin All Rights Reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++*/
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include "AudioDeviceList.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <AvailabilityMacros.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#if MAC_OS_X_VERSION_MIN_REQUIRED > 1080
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  #define verify_noerr __Verify_noErr
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDeviceList::AudioDeviceList(bool forInput)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  : mForInput(forInput)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  BuildList();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++AudioDeviceList::~AudioDeviceList()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++void AudioDeviceList::BuildList()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++{
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  mDeviceList.clear();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  mDeviceDict.clear();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  UInt32 propsize;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioObjectPropertyAddress theAddress = { kAudioHardwarePropertyDevices,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                                                    kAudioObjectPropertyScopeGlobal,
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                                                    kAudioObjectPropertyElementMaster
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                                                                  };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  verify_noerr(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  int nDevices = propsize / sizeof(AudioDeviceID);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioDeviceID *devids = new AudioDeviceID[nDevices];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  verify_noerr(AudioObjectGetPropertyData(kAudioObjectSystemObject, &theAddress, 0, NULL, &propsize, devids));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  for (int i = 0; i < nDevices; ++i) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          AudioDevice dev(devids[i], true, mForInput);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (dev.CountChannels() > 0) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  Device d;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  d.mID = devids[i];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  dev.GetName(d.mName, sizeof(d.mName));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  mDeviceList.push_back(d);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  QString name = QString::fromUtf8(d.mName);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  if (!mDeviceDict.contains(name)) {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          mDeviceDict[name] = d.mID;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  delete[] devids;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++}
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/3rdparty/CoreAudio/AudioDeviceList.h src/modules/PortAudio/3rdparty/CoreAudio/AudioDeviceList.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+new file mode 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 00000000..89ff3a87
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/3rdparty/CoreAudio/AudioDeviceList.h
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -0,0 +1,82 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++/*
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++     File: AudioDeviceList.h 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Adapted from the CAPlayThough example
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  Version: 1.2.2 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Inc. ("Apple") in consideration of your agreement to the following 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ terms, and your use, installation, modification or redistribution of 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ this Apple software constitutes acceptance of these terms.  If you do 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ not agree with these terms, please do not use, install, modify or 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ redistribute this Apple software. 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ In consideration of your agreement to abide by the following terms, and 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ subject to these terms, Apple grants you a personal, non-exclusive 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ license, under Apple's copyrights in this original Apple software (the 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ "Apple Software"), to use, reproduce, modify and redistribute the Apple 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Software, with or without modifications, in source and/or binary forms; 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ provided that if you redistribute the Apple Software in its entirety and 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ without modifications, you must retain this notice and the following 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ text and disclaimers in all such redistributions of the Apple Software. 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Neither the name, trademarks, service marks or logos of Apple Inc. may 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ be used to endorse or promote products derived from the Apple Software 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ without specific prior written permission from Apple.  Except as 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ expressly stated in this notice, no other rights or licenses, express or 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ implied, are granted by Apple herein, including but not limited to any 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ patent rights that may be infringed by your derivative works or by other 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ works in which the Apple Software may be incorporated. 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ The Apple Software is provided by Apple on an "AS IS" basis.  APPLE 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ POSSIBILITY OF SUCH DAMAGE. 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Copyright (C) 2013 Apple Inc. All Rights Reserved. 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++ Copyright (C) 2017,18 René J.V. Bertin All Rights Reserved.
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++*/
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifndef __AudioDeviceList_h__
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#define __AudioDeviceList_h__
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <CoreServices/CoreServices.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <CoreAudio/CoreAudio.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include "AudioDevice.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QList>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QHash>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++class AudioDeviceList {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++public:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  struct Device {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          char mName[256];
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          AudioDeviceID mID;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  typedef QList<Device> DeviceList;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  typedef QHash<QString,AudioDeviceID> DeviceDict;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioDeviceList(bool forInput=false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  ~AudioDeviceList();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  DeviceList &GetList() { return mDeviceList; }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  DeviceDict &GetDict() { return mDeviceDict; }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++protected:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  void BuildList();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  bool mForInput;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  DeviceList mDeviceList;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  DeviceDict mDeviceDict;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif // __AudioDeviceList_h__
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/CMakeLists.txt src/modules/PortAudio/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index b0310d5c..add7286f 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/PortAudio/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -27,6 +27,13 @@ set(PortAudio_SRC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     PortAudioWriter.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ )
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++if(APPLE)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    list(APPEND PortAudio_SRC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        3rdparty/CoreAudio/AudioDevice.mm
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        3rdparty/CoreAudio/AudioDeviceList.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    )
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++endif()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ set(PortAudio_RESOURCES
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     icon.qrc
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ )
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -69,6 +76,9 @@ if(WIN32 AND CUSTOM_PORTAUDIO_LIBRARIES)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     target_link_libraries(${PROJECT_NAME} ${CUSTOM_PORTAUDIO_LIBRARIES})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ else()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     target_link_libraries(${PROJECT_NAME} ${LIBPORTAUDIO_LIBRARIES})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    if(APPLE)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++        target_link_libraries(${PROJECT_NAME} "-framework CoreAudio -framework Foundation")
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    endif()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ endif()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${MODULES_INSTALL_PATH})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/PortAudio.cpp src/modules/PortAudio/PortAudio.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index a130fe25..5ee2845d 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/PortAudio/PortAudio.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/PortAudio.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -88,10 +88,19 @@ ModuleSettingsWidget::ModuleSettingsWidget(Module &module) :
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int idx = devicesB->findText(sets().getString("OutputDevice"));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   devicesB->setCurrentIndex(idx < 0 ? 0 : idx);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  bitPerfect = new QCheckBox(tr("Bit-perfect audio"));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  bitPerfect->setChecked(sets().getBool("BitPerfect"));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  bitPerfect->setToolTip(tr("This sets the selected output device to the sample rate of the content being played"));
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   QFormLayout *layout = new QFormLayout(this);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   layout->addRow(enabledB);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   layout->addRow(tr("Playback device") + ": ", devicesB);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   layout->addRow(tr("Delay") + ": ", delayB);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  layout->addRow(bitPerfect);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ void ModuleSettingsWidget::saveSettings()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -99,4 +108,7 @@ void ModuleSettingsWidget::saveSettings()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   sets().set("WriterEnabled", enabledB->isChecked());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   sets().set("OutputDevice", devicesB->currentIndex() == 0 ? QString() : devicesB->currentText());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   sets().set("Delay", delayB->value());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  sets().set("BitPerfect", bitPerfect->isChecked());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/PortAudio.hpp src/modules/PortAudio/PortAudio.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 5d3d57d7..98a72471 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/PortAudio/PortAudio.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/PortAudio.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -53,4 +53,7 @@ private:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   QCheckBox *enabledB;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   QComboBox *devicesB;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   QDoubleSpinBox *delayB;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  QCheckBox *bitPerfect;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/PortAudioWriter.cpp src/modules/PortAudio/PortAudioWriter.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 38dc27d9..a4a6619b 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/PortAudio/PortAudioWriter.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/PortAudioWriter.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -24,11 +24,17 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  #include "3rdparty/CoreAudio/AudioDeviceList.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  #include "3rdparty/CoreAudio/AudioDevice.h"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   #define DEFAULT_HIGH_AUDIO_DELAY 0.2
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #else
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   #define DEFAULT_HIGH_AUDIO_DELAY 0.1
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    #define QStringLiteral QString::fromUtf8
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ PortAudioWriter::PortAudioWriter(Module &module) :
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   stream(NULL),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   sample_rate(0),
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -47,6 +53,13 @@ PortAudioWriter::PortAudioWriter(Module &module) :
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ PortAudioWriter::~PortAudioWriter()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  if (coreAudioDevice)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          coreAudioDevice->ResetNominalSampleRate();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          delete coreAudioDevice;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   close();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -191,7 +204,30 @@ void PortAudioWriter::pause()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ QString PortAudioWriter::name() const
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  return PortAudioWriterName;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  QString name = PortAudioWriterName;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  if (stream)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (const PaDeviceInfo *dInfo = Pa_GetDeviceInfo(outputParameters.device))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  name += QStringLiteral(" (%1").arg(dInfo->name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  if (const PaHostApiInfo *hInfo = Pa_GetHostApiInfo(dInfo->hostApi))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          name += QStringLiteral("; %1").arg(hInfo->name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  name += ")";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (const PaStreamInfo *strInfo = Pa_GetStreamInfo(stream))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  name += QStringLiteral(", %1Hz").arg(strInfo->sampleRate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (coreAudioDevice)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  name += QStringLiteral(" -> %1Hz").arg(coreAudioDevice->CurrentNominalSampleRate());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  return name;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ bool PortAudioWriter::open()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -209,6 +245,21 @@ bool PortAudioWriter::openStream()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           stream = newStream;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           outputLatency = Pa_GetStreamInfo(stream)->outputLatency;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           modParam("delay", outputLatency);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (sets().getBool("BitPerfect"))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  const QString devName(Pa_GetDeviceInfo(outputParameters.device)->name);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  const AudioDeviceList::DeviceDict devDict = AudioDeviceList().GetDict();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  if (devDict.contains(devName))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          coreAudioDevice = AudioDevice::GetDevice(devDict[devName], false, coreAudioDevice);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          if (coreAudioDevice)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                  coreAudioDevice->SetNominalSampleRate(sample_rate);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           return true;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   return false;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/PortAudio/PortAudioWriter.hpp src/modules/PortAudio/PortAudioWriter.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 9d4f617f..009dc013 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/PortAudio/PortAudioWriter.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/PortAudio/PortAudioWriter.hpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -25,6 +25,10 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QCoreApplication>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++class AudioDevice;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ class PortAudioWriter : public Writer
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   Q_DECLARE_TR_FUNCTIONS(PortAudioWriter)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -66,6 +70,9 @@ private:
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   double outputLatency;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   bool err, fullBufferReached;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   int underflows;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifdef Q_OS_MAC
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  AudioDevice *coreAudioDevice = nullptr;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ };
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #define PortAudioWriterName "PortAudio"
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/multimedia/QMPlay2/files/0010-Do-not-force-fetching-yt-dlp-it-does-not-work-use-Ma.patch b/multimedia/QMPlay2/files/0010-Do-not-force-fetching-yt-dlp-it-does-not-work-use-Ma.patch
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 00000000000..2bcb4340a81
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/multimedia/QMPlay2/files/0010-Do-not-force-fetching-yt-dlp-it-does-not-work-use-Ma.patch
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,61 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From 2a99e595ce53c0ef1ecef0fad67319dff0001fec Mon Sep 17 00:00:00 2001
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From: Sergey Fedorov <vital.had@gmail.com>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Date: Sat, 30 Nov 2024 03:04:33 +0800
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Subject: [PATCH] =?UTF-8?q?Do=20not=20force=20fetching=20yt-dlp,=20i?=
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ =?UTF-8?q?t=20does=20not=20work;=20use=20MacPorts=E2=80=99=20one?=
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+MIME-Version: 1.0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Content-Type: text/plain; charset=UTF-8
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Content-Transfer-Encoding: 8bit
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+---
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/qmplay2/YouTubeDL.cpp | 9 +++++++++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 1 file changed, 9 insertions(+)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/qmplay2/YouTubeDL.cpp src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 98071fea..b87f64af 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -28,6 +28,11 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QReadWriteLock>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QFile>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++/* Avoid downloading yt-dlp, it fails to work correctly. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#ifndef BUNDLED_YTDLP
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#define BUNDLED_YTDLP 0
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ constexpr char g_name[] = "YouTubeDL";
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ static QReadWriteLock g_lock;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -263,6 +268,7 @@ QStringList YouTubeDL::exec(const QString &url, const QStringList &args, QString
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           if (error.indexOf("ERROR: ") == 0)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   error.remove(0, 7);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  #if BUNDLED_YTDLP
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   if (canUpdate && !error.contains("said:")) // Probably update can fix the error, so do it!
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           if (!doLock(Lock::Write, true)) // Unlock for read and lock for write
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -300,6 +306,7 @@ QStringList YouTubeDL::exec(const QString &url, const QStringList &args, QString
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           if (!doLock(Lock::Read, true)) // Unlock for write and lock for read
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   return {};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  #endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   finishWithError(error);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   return {};
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -317,6 +324,7 @@ QStringList YouTubeDL::exec(const QString &url, const QStringList &args, QString
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           g_lock.unlock(); // Unlock for read
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           return result;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#if BUNDLED_YTDLP
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   else if (canUpdate && !m_aborted && m_process.error() == QProcess::FailedToStart)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           const QString downloadUrl = "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp"
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -361,6 +369,7 @@ QStringList YouTubeDL::exec(const QString &url, const QStringList &args, QString
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                   QMPlay2Core.setWorking(false);
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+           }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#endif
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   g_lock.unlock(); // Unlock for read or for write (if download has failed)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #else
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/multimedia/QMPlay2/files/0011-YouTubeDL-move-to-QJson.patch b/multimedia/QMPlay2/files/0011-YouTubeDL-move-to-QJson.patch
</span>new file mode 100644
<span style='display:block; white-space:pre;color:#808080;'>index 00000000000..a0fe30dead1
</span><span style='display:block; white-space:pre;background:#ffe0e0;'>--- /dev/null
</span><span style='display:block; white-space:pre;background:#e0e0ff;'>+++ b/multimedia/QMPlay2/files/0011-YouTubeDL-move-to-QJson.patch
</span><span style='display:block; white-space:pre;background:#e0e0e0;'>@@ -0,0 +1,88 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From a22054bb93586bdaeb1e29f90ef2367d90fe2d34 Mon Sep 17 00:00:00 2001
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+From: Sergey Fedorov <vital.had@gmail.com>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Date: Fri, 29 Nov 2024 10:41:22 +0800
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+Subject: [PATCH] YouTubeDL: move to QJson
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+---
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/modules/Extensions/MediaBrowser/ProstoPleer.cpp |  7 ++++---
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/qmplay2/CMakeLists.txt                          |  3 ++-
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ src/qmplay2/YouTubeDL.cpp                           | 13 +++++++------
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 3 files changed, 13 insertions(+), 10 deletions(-)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/modules/Extensions/MediaBrowser/ProstoPleer.cpp src/modules/Extensions/MediaBrowser/ProstoPleer.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index cb3c287d..c35bd5d2 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/modules/Extensions/MediaBrowser/ProstoPleer.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/modules/Extensions/MediaBrowser/ProstoPleer.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -21,9 +21,10 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QMPlay2Extensions.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <NetworkAccess.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <Functions.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-#include <Json11.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonDocument.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QTextDocument>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonObject.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QHeaderView>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QTreeWidget>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QAction>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -187,8 +188,8 @@ bool ProstoPleer::convertAddress(const QString &prefix, const QString &url, cons
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           IOController<NetworkReply> &netReply = ioCtrl->toRef<NetworkReply>();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           if (net.startAndWait(netReply, QString("%1/site_api/files/get_url?id=%2").arg(g_url, fileId.mid(idx + 1))))
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                           {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  const Json json = Json::parse(netReply->readAll());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                                  const QString tmpStreamUrl = json["track_link"].string_value();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                  const QJsonDocument json = QJsonDocument::fromJson(netReply->readAll());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                                  const QString tmpStreamUrl = json.object()["track_link"].toString();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   if (!tmpStreamUrl.isEmpty())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                           *streamUrl = tmpStreamUrl;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+                                   netReply.clear();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/qmplay2/CMakeLists.txt src/qmplay2/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index 01932ce6..270ad9cf 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/qmplay2/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/qmplay2/CMakeLists.txt
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -212,7 +212,8 @@ if(USE_QT5)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+         target_link_libraries(${PROJECT_NAME} ${APPKIT_LIBRARY})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+     endif()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ else()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-    target_link_libraries(${PROJECT_NAME} Qt4::QtCore Qt4::QtGui ${DBUS})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    add_definitions(-I/opt/local/include/QJson4)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++    target_link_libraries(${PROJECT_NAME} Qt4::QtCore Qt4::QtGui QJson4 ${DBUS})
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ endif()
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ if(WIN32)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+diff --git src/qmplay2/YouTubeDL.cpp src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+index b87f64af..fb7e72fc 100644
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+--- src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++++ src/qmplay2/YouTubeDL.cpp
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -21,11 +21,13 @@
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <NetworkAccess.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QMPlay2Core.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <Version.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-#include <Json11.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <Functions.hpp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QRegExp>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QReadWriteLock>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonDocument.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonObject.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++#include <QJsonArray.h>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ #include <QFile>
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ /* Avoid downloading yt-dlp, it fails to work correctly. */
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+@@ -41,12 +43,11 @@ static QReadWriteLock g_lock;
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ static void exportCookiesFromJSON(const QString &jsonData, const QString &url)
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  const Json json = Json::parse(jsonData.toUtf8());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  const QByteArray urlData = url.toUtf8();
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-  for (const Json &formats : json["formats"].array_items())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  const QJsonDocument json = QJsonDocument::fromJson(jsonData.toUtf8());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++  for (const QJsonValue &formats : json.object()["formats"].toArray())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   {
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-          if (urlData == formats["url"].string_value())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+-                  QMPlay2Core.addCookies(url, formats["http_headers"]["Cookie"].string_value());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++          if (url == formats.toObject()["url"].toString())
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>++                  QMPlay2Core.addCookies(url, formats.toObject()["http_headers"].toObject()["Cookie"].toString().toUtf8());
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+   }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ }
</span><span style='display:block; white-space:pre;background:#e0ffe0;'>+ 
</span><span style='display:block; white-space:pre;color:#808080;'>diff --git a/multimedia/QMPlay2/files/0007-Fix-Qt-paths.patch b/multimedia/QMPlay2/files/0012-Fix-Qt-paths.patch
</span>similarity index 100%
rename from multimedia/QMPlay2/files/0007-Fix-Qt-paths.patch
rename to multimedia/QMPlay2/files/0012-Fix-Qt-paths.patch
</pre><pre style='margin:0'>

</pre>