{"action":"created","comment":{"url":"https://api.github.com/repos/citra-emu/citra/pulls/comments/50831928","id":50831928,"diff_hunk":"@@ -0,0 +1,343 @@\n+// Copyright 2016 Citra Emulator Project\n+// Licensed under GPLv2 or any later version\n+// Refer to the license.txt file included.\n+\n+#include <AL/al.h>\n+#include <AL/alc.h>\n+#include <AL/alext.h>\n+\n+#include \"common/logging/log.h\"\n+#include \"common/math_util.h\"\n+\n+#include \"core/audio/audio.h\"\n+\n+#include <algorithm>\n+#include <array>\n+#include <memory>\n+#include <queue>\n+\n+namespace Audio {\n+\n+using ALCDevicePointer = std::unique_ptr<ALCdevice, decltype(&alcCloseDevice)>;\n+using ALCContextPointer = std::unique_ptr<ALCcontext, decltype(&alcDestroyContext)>;\n+\n+static ALCDevicePointer device = ALCDevicePointer(nullptr, nullptr);\n+static ALCContextPointer context = ALCContextPointer(nullptr, nullptr);\n+\n+static const int BASE_SAMPLE_RATE = 22050;\n+\n+struct Buffer {\n+    u16 id; ///< buffer_id that userland gives us\n+    ALuint buffer;\n+    bool is_looping;\n+    bool operator < (const Buffer& other) const {\n+        // We want things with lower id to appear first, unless we have wraparound.\n+        // priority_queue puts a before b when b < a.\n+        // Should perhaps be a functor instead.\n+        if ((other.id - id) > 1000) return true;\n+        if ((id - other.id) > 1000) return false;\n+        return id > other.id;\n+    }\n+};\n+\n+struct AdpcmState {\n+    // Two historical samples from previous processed buffer\n+    s16 yn1; ///< y[n-1]\n+    s16 yn2; ///< y[n-2]\n+};\n+\n+// GC-ADPCM with scale factor and variable coefficients.\n+// Frames are 8 bytes long containing 14 samples each.\n+// Samples are 4 bits (one nybble) long.\n+static std::vector<s16> DecodeADPCM(const u8 * const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, AdpcmState& state) {\n+    constexpr size_t FRAME_LEN = 8;\n+    constexpr size_t SAMPLES_PER_FRAME = 14;\n+    constexpr int SIGNED_NYBBLES[16] = {0,1,2,3,4,5,6,7,-8,-7,-6,-5,-4,-3,-2,-1};\n+\n+    std::vector<s16> ret(sample_count);\n+    int yn1 = state.yn1, yn2 = state.yn2;\n+\n+    const int NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME-1)) / SAMPLES_PER_FRAME; // Round up.\n+    for (int framei = 0; framei < NUM_FRAMES; framei++) {\n+        int frame_header = data[framei * FRAME_LEN];\n+        int scale = 1 << (frame_header & 0xF);\n+        int idx = (frame_header >> 4) & 0x7;\n+\n+        // Coefficients are fixed point with 11 bits fractional part.\n+        int coef1 = adpcm_coeff[idx * 2 + 0];\n+        int coef2 = adpcm_coeff[idx * 2 + 1];\n+\n+        // Decodes an audio sample. One nybble produces one s16 sample.\n+        auto decode_sample = [&](int nybble) -> s16 {\n+            int xn = nybble * scale;\n+            // We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back.\n+            // 0x400 == 0.5 in 11 bit fixed point.\n+            // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]\n+            int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;\n+            // Clamp to output range.\n+            val = MathUtil::Clamp(val, -32768, 32767);\n+            // Advance output feedback.\n+            yn2 = yn1;\n+            yn1 = val;\n+            return (s16)val;\n+        };\n+\n+        int outputi = framei * SAMPLES_PER_FRAME;\n+        int datai = framei * FRAME_LEN + 1;\n+        for (int i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {\n+            ret[outputi++] = decode_sample(SIGNED_NYBBLES[data[datai] & 0xF]);\n+            ret[outputi++] = decode_sample(SIGNED_NYBBLES[data[datai] >> 4]);\n+            datai++;\n+        }\n+    }\n+\n+    state.yn1 = yn1;\n+    state.yn2 = yn2;\n+\n+    return ret;\n+}\n+\n+struct OutputChannel {\n+    ~OutputChannel() {\n+        alDeleteSources(1, &source);\n+        while (!queue.empty()) {\n+            alDeleteBuffers(1, &queue.top().buffer);\n+            queue.pop();\n+        }\n+        while (!playing.empty()) {\n+            alDeleteBuffers(1, &playing.front().buffer);\n+            playing.pop();\n+        }\n+    }\n+\n+    ALuint source;                     ///< Each channel has it's own output, we lean on OpenAL to do our mixing.\n+\n+    // Configuration\n+    int mono_or_stereo;                ///< Value from userland. 1 == mono, 2 == stereo, other == ???\n+    Format format;\n+    bool enabled;                      ///< Userland wants us to remind them we have enabled this channel.\n+    bool was_fed_data;                 ///< Userland wants to know if we have been fed data.\n+\n+    // Buffer management\n+    std::priority_queue<Buffer> queue; ///< Things we have gotten from userland we haven't queued onto `source` yet.\n+    std::queue<Buffer> playing;        ///< Things we have queued onto `source`.\n+    u16 last_buffer_id;                ///< Userland wants us to report back what was the thing we last played.\n+\n+    // For ADPCM decoding use.\n+    std::array<s16, 16> adpcm_coeffs;\n+    AdpcmState adpcm_state;\n+};\n+\n+static std::array<OutputChannel, 24> chans;\n+\n+int InitAL() {\n+    device = ALCDevicePointer(alcOpenDevice(nullptr), &alcCloseDevice);\n+    if (!device) {\n+        LOG_CRITICAL(Audio, \"Could not open a device!\");\n+        return 1;\n+    }\n+\n+    context = ALCContextPointer(alcCreateContext(device.get(), nullptr), &alcDestroyContext);\n+    if (context == nullptr || alcMakeContextCurrent(context.get()) == ALC_FALSE) {\n+        if (context != nullptr) {","path":"src/core/audio/audio.cpp","position":142,"original_position":142,"commit_id":"726c7d93c15336690fe7e32a7a7a66b861176aa7","original_commit_id":"726c7d93c15336690fe7e32a7a7a66b861176aa7","user":{"login":"lioncash","id":712067,"avatar_url":"https://avatars.githubusercontent.com/u/712067?v=3","gravatar_id":"","url":"https://api.github.com/users/lioncash","html_url":"https://github.com/lioncash","followers_url":"https://api.github.com/users/lioncash/followers","following_url":"https://api.github.com/users/lioncash/following{/other_user}","gists_url":"https://api.github.com/users/lioncash/gists{/gist_id}","starred_url":"https://api.github.com/users/lioncash/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lioncash/subscriptions","organizations_url":"https://api.github.com/users/lioncash/orgs","repos_url":"https://api.github.com/users/lioncash/repos","events_url":"https://api.github.com/users/lioncash/events{/privacy}","received_events_url":"https://api.github.com/users/lioncash/received_events","type":"User","site_admin":false},"body":"This can just be\r\n\r\n```cpp\r\ncontext.reset();\r\ndevice.reset();\r\n```\r\n\r\nif order of deallocation is important (reset doesn't call the unique_ptr's deleter if it contains nullptr), otherwise they can just be removed entirely","created_at":"2016-01-26T13:08:33Z","updated_at":"2016-01-26T13:08:33Z","html_url":"https://github.com/citra-emu/citra/pull/1374#discussion_r50831928","pull_request_url":"https://api.github.com/repos/citra-emu/citra/pulls/1374","_links":{"self":{"href":"https://api.github.com/repos/citra-emu/citra/pulls/comments/50831928"},"html":{"href":"https://github.com/citra-emu/citra/pull/1374#discussion_r50831928"},"pull_request":{"href":"https://api.github.com/repos/citra-emu/citra/pulls/1374"}}},"pull_request":{"url":"https://api.github.com/repos/citra-emu/citra/pulls/1374","id":57071903,"html_url":"https://github.com/citra-emu/citra/pull/1374","diff_url":"https://github.com/citra-emu/citra/pull/1374.diff","patch_url":"https://github.com/citra-emu/citra/pull/1374.patch","issue_url":"https://api.github.com/repos/citra-emu/citra/issues/1374","number":1374,"state":"open","locked":false,"title":"RFC: Audio","user":{"login":"MerryMage","id":8682882,"avatar_url":"https://avatars.githubusercontent.com/u/8682882?v=3","gravatar_id":"","url":"https://api.github.com/users/MerryMage","html_url":"https://github.com/MerryMage","followers_url":"https://api.github.com/users/MerryMage/followers","following_url":"https://api.github.com/users/MerryMage/following{/other_user}","gists_url":"https://api.github.com/users/MerryMage/gists{/gist_id}","starred_url":"https://api.github.com/users/MerryMage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MerryMage/subscriptions","organizations_url":"https://api.github.com/users/MerryMage/orgs","repos_url":"https://api.github.com/users/MerryMage/repos","events_url":"https://api.github.com/users/MerryMage/events{/privacy}","received_events_url":"https://api.github.com/users/MerryMage/received_events","type":"User","site_admin":false},"body":"Implementation raw audio output. DSP effects not yet implemented.\r\n\r\nFirst contribution to a large C++ project.\r\n\r\nHere are a few things I would like to do before a merge:\r\n- [ ] Proper cmake for OpenAL-Soft. [Binary package (Windows)](http://kcat.strangesoft.net/openal-binaries/openal-soft-1.17.2-bin.zip), [Source repo (Linux / OS X)](https://github.com/kcat/openal-soft/tree/openal-soft-1.17.1).\r\n- [ ] Implement audio mixing (stereo only).\r\n\r\nThings I would like to have comments on:\r\n- Is everything in the right place?\r\n- Is OpenAL-Soft acceptable?\r\n- Should multple possible audio outputs be possible in the future? (NullAudio?)\r\n- Should there be a config option to disable audio?\r\n\r\nSome things I haven't done and might do in the future (note to self):\r\n- Wav dumper.\r\n- Figure out why some audio clips don't play.\r\n- Implement IIR filter.\r\n- Implement aux and feedback.\r\n- Implement full audio mixing.\r\n- Implement rate.\r\n- Figure out everything else.","created_at":"2016-01-25T13:37:54Z","updated_at":"2016-01-26T13:08:33Z","closed_at":null,"merged_at":null,"merge_commit_sha":"5cc0f82e2815b2b9276242b318f390f2ce2b28f8","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/citra-emu/citra/pulls/1374/commits","review_comments_url":"https://api.github.com/repos/citra-emu/citra/pulls/1374/comments","review_comment_url":"https://api.github.com/repos/citra-emu/citra/pulls/comments{/number}","comments_url":"https://api.github.com/repos/citra-emu/citra/issues/1374/comments","statuses_url":"https://api.github.com/repos/citra-emu/citra/statuses/726c7d93c15336690fe7e32a7a7a66b861176aa7","head":{"label":"MerryMage:dsp_hle","ref":"dsp_hle","sha":"726c7d93c15336690fe7e32a7a7a66b861176aa7","user":{"login":"MerryMage","id":8682882,"avatar_url":"https://avatars.githubusercontent.com/u/8682882?v=3","gravatar_id":"","url":"https://api.github.com/users/MerryMage","html_url":"https://github.com/MerryMage","followers_url":"https://api.github.com/users/MerryMage/followers","following_url":"https://api.github.com/users/MerryMage/following{/other_user}","gists_url":"https://api.github.com/users/MerryMage/gists{/gist_id}","starred_url":"https://api.github.com/users/MerryMage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MerryMage/subscriptions","organizations_url":"https://api.github.com/users/MerryMage/orgs","repos_url":"https://api.github.com/users/MerryMage/repos","events_url":"https://api.github.com/users/MerryMage/events{/privacy}","received_events_url":"https://api.github.com/users/MerryMage/received_events","type":"User","site_admin":false},"repo":{"id":49902315,"name":"citra","full_name":"MerryMage/citra","owner":{"login":"MerryMage","id":8682882,"avatar_url":"https://avatars.githubusercontent.com/u/8682882?v=3","gravatar_id":"","url":"https://api.github.com/users/MerryMage","html_url":"https://github.com/MerryMage","followers_url":"https://api.github.com/users/MerryMage/followers","following_url":"https://api.github.com/users/MerryMage/following{/other_user}","gists_url":"https://api.github.com/users/MerryMage/gists{/gist_id}","starred_url":"https://api.github.com/users/MerryMage/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MerryMage/subscriptions","organizations_url":"https://api.github.com/users/MerryMage/orgs","repos_url":"https://api.github.com/users/MerryMage/repos","events_url":"https://api.github.com/users/MerryMage/events{/privacy}","received_events_url":"https://api.github.com/users/MerryMage/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/MerryMage/citra","description":"Nintendo 3DS emulator/debugger","fork":true,"url":"https://api.github.com/repos/MerryMage/citra","forks_url":"https://api.github.com/repos/MerryMage/citra/forks","keys_url":"https://api.github.com/repos/MerryMage/citra/keys{/key_id}","collaborators_url":"https://api.github.com/repos/MerryMage/citra/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/MerryMage/citra/teams","hooks_url":"https://api.github.com/repos/MerryMage/citra/hooks","issue_events_url":"https://api.github.com/repos/MerryMage/citra/issues/events{/number}","events_url":"https://api.github.com/repos/MerryMage/citra/events","assignees_url":"https://api.github.com/repos/MerryMage/citra/assignees{/user}","branches_url":"https://api.github.com/repos/MerryMage/citra/branches{/branch}","tags_url":"https://api.github.com/repos/MerryMage/citra/tags","blobs_url":"https://api.github.com/repos/MerryMage/citra/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/MerryMage/citra/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/MerryMage/citra/git/refs{/sha}","trees_url":"https://api.github.com/repos/MerryMage/citra/git/trees{/sha}","statuses_url":"https://api.github.com/repos/MerryMage/citra/statuses/{sha}","languages_url":"https://api.github.com/repos/MerryMage/citra/languages","stargazers_url":"https://api.github.com/repos/MerryMage/citra/stargazers","contributors_url":"https://api.github.com/repos/MerryMage/citra/contributors","subscribers_url":"https://api.github.com/repos/MerryMage/citra/subscribers","subscription_url":"https://api.github.com/repos/MerryMage/citra/subscription","commits_url":"https://api.github.com/repos/MerryMage/citra/commits{/sha}","git_commits_url":"https://api.github.com/repos/MerryMage/citra/git/commits{/sha}","comments_url":"https://api.github.com/repos/MerryMage/citra/comments{/number}","issue_comment_url":"https://api.github.com/repos/MerryMage/citra/issues/comments{/number}","contents_url":"https://api.github.com/repos/MerryMage/citra/contents/{+path}","compare_url":"https://api.github.com/repos/MerryMage/citra/compare/{base}...{head}","merges_url":"https://api.github.com/repos/MerryMage/citra/merges","archive_url":"https://api.github.com/repos/MerryMage/citra/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/MerryMage/citra/downloads","issues_url":"https://api.github.com/repos/MerryMage/citra/issues{/number}","pulls_url":"https://api.github.com/repos/MerryMage/citra/pulls{/number}","milestones_url":"https://api.github.com/repos/MerryMage/citra/milestones{/number}","notifications_url":"https://api.github.com/repos/MerryMage/citra/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/MerryMage/citra/labels{/name}","releases_url":"https://api.github.com/repos/MerryMage/citra/releases{/id}","deployments_url":"https://api.github.com/repos/MerryMage/citra/deployments","created_at":"2016-01-18T20:13:00Z","updated_at":"2016-01-26T06:34:03Z","pushed_at":"2016-01-26T12:53:32Z","git_url":"git://github.com/MerryMage/citra.git","ssh_url":"git@github.com:MerryMage/citra.git","clone_url":"https://github.com/MerryMage/citra.git","svn_url":"https://github.com/MerryMage/citra","homepage":null,"size":11830,"stargazers_count":3,"watchers_count":3,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":3,"default_branch":"master"}},"base":{"label":"citra-emu:master","ref":"master","sha":"8b3994e9e4629423960f9ba22e5df01afd5b724b","user":{"login":"citra-emu","id":7605670,"avatar_url":"https://avatars.githubusercontent.com/u/7605670?v=3","gravatar_id":"","url":"https://api.github.com/users/citra-emu","html_url":"https://github.com/citra-emu","followers_url":"https://api.github.com/users/citra-emu/followers","following_url":"https://api.github.com/users/citra-emu/following{/other_user}","gists_url":"https://api.github.com/users/citra-emu/gists{/gist_id}","starred_url":"https://api.github.com/users/citra-emu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/citra-emu/subscriptions","organizations_url":"https://api.github.com/users/citra-emu/orgs","repos_url":"https://api.github.com/users/citra-emu/repos","events_url":"https://api.github.com/users/citra-emu/events{/privacy}","received_events_url":"https://api.github.com/users/citra-emu/received_events","type":"Organization","site_admin":false},"repo":{"id":19866730,"name":"citra","full_name":"citra-emu/citra","owner":{"login":"citra-emu","id":7605670,"avatar_url":"https://avatars.githubusercontent.com/u/7605670?v=3","gravatar_id":"","url":"https://api.github.com/users/citra-emu","html_url":"https://github.com/citra-emu","followers_url":"https://api.github.com/users/citra-emu/followers","following_url":"https://api.github.com/users/citra-emu/following{/other_user}","gists_url":"https://api.github.com/users/citra-emu/gists{/gist_id}","starred_url":"https://api.github.com/users/citra-emu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/citra-emu/subscriptions","organizations_url":"https://api.github.com/users/citra-emu/orgs","repos_url":"https://api.github.com/users/citra-emu/repos","events_url":"https://api.github.com/users/citra-emu/events{/privacy}","received_events_url":"https://api.github.com/users/citra-emu/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/citra-emu/citra","description":"Nintendo 3DS emulator/debugger","fork":false,"url":"https://api.github.com/repos/citra-emu/citra","forks_url":"https://api.github.com/repos/citra-emu/citra/forks","keys_url":"https://api.github.com/repos/citra-emu/citra/keys{/key_id}","collaborators_url":"https://api.github.com/repos/citra-emu/citra/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/citra-emu/citra/teams","hooks_url":"https://api.github.com/repos/citra-emu/citra/hooks","issue_events_url":"https://api.github.com/repos/citra-emu/citra/issues/events{/number}","events_url":"https://api.github.com/repos/citra-emu/citra/events","assignees_url":"https://api.github.com/repos/citra-emu/citra/assignees{/user}","branches_url":"https://api.github.com/repos/citra-emu/citra/branches{/branch}","tags_url":"https://api.github.com/repos/citra-emu/citra/tags","blobs_url":"https://api.github.com/repos/citra-emu/citra/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/citra-emu/citra/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/citra-emu/citra/git/refs{/sha}","trees_url":"https://api.github.com/repos/citra-emu/citra/git/trees{/sha}","statuses_url":"https://api.github.com/repos/citra-emu/citra/statuses/{sha}","languages_url":"https://api.github.com/repos/citra-emu/citra/languages","stargazers_url":"https://api.github.com/repos/citra-emu/citra/stargazers","contributors_url":"https://api.github.com/repos/citra-emu/citra/contributors","subscribers_url":"https://api.github.com/repos/citra-emu/citra/subscribers","subscription_url":"https://api.github.com/repos/citra-emu/citra/subscription","commits_url":"https://api.github.com/repos/citra-emu/citra/commits{/sha}","git_commits_url":"https://api.github.com/repos/citra-emu/citra/git/commits{/sha}","comments_url":"https://api.github.com/repos/citra-emu/citra/comments{/number}","issue_comment_url":"https://api.github.com/repos/citra-emu/citra/issues/comments{/number}","contents_url":"https://api.github.com/repos/citra-emu/citra/contents/{+path}","compare_url":"https://api.github.com/repos/citra-emu/citra/compare/{base}...{head}","merges_url":"https://api.github.com/repos/citra-emu/citra/merges","archive_url":"https://api.github.com/repos/citra-emu/citra/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/citra-emu/citra/downloads","issues_url":"https://api.github.com/repos/citra-emu/citra/issues{/number}","pulls_url":"https://api.github.com/repos/citra-emu/citra/pulls{/number}","milestones_url":"https://api.github.com/repos/citra-emu/citra/milestones{/number}","notifications_url":"https://api.github.com/repos/citra-emu/citra/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/citra-emu/citra/labels{/name}","releases_url":"https://api.github.com/repos/citra-emu/citra/releases{/id}","deployments_url":"https://api.github.com/repos/citra-emu/citra/deployments","created_at":"2014-05-16T18:33:15Z","updated_at":"2016-01-26T13:02:33Z","pushed_at":"2016-01-26T12:53:32Z","git_url":"git://github.com/citra-emu/citra.git","ssh_url":"git@github.com:citra-emu/citra.git","clone_url":"https://github.com/citra-emu/citra.git","svn_url":"https://github.com/citra-emu/citra","homepage":null,"size":18709,"stargazers_count":1454,"watchers_count":1454,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":341,"mirror_url":null,"open_issues_count":144,"forks":341,"open_issues":144,"watchers":1454,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/citra-emu/citra/pulls/1374"},"html":{"href":"https://github.com/citra-emu/citra/pull/1374"},"issue":{"href":"https://api.github.com/repos/citra-emu/citra/issues/1374"},"comments":{"href":"https://api.github.com/repos/citra-emu/citra/issues/1374/comments"},"review_comments":{"href":"https://api.github.com/repos/citra-emu/citra/pulls/1374/comments"},"review_comment":{"href":"https://api.github.com/repos/citra-emu/citra/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/citra-emu/citra/pulls/1374/commits"},"statuses":{"href":"https://api.github.com/repos/citra-emu/citra/statuses/726c7d93c15336690fe7e32a7a7a66b861176aa7"}}}}