{"action":"created","comment":{"url":"https://api.github.com/repos/copy/v86/pulls/comments/175251449","pull_request_review_id":104760094,"id":175251449,"diff_hunk":"@@ -1169,19 +1218,196 @@ function register_mixer_write(address, handler)\n     MIXER_WRITE_HANDLERS[address] = handler;\n }\n \n+// Legacy registers map each nibble to the last 4 bits of the new registers\n+function register_mixer_legacy(address_old, address_new_left, address_new_right)\n+{\n+    /** @this {SB16} */\n+    MIXER_READ_HANDLERS[address_old] = function()\n+    {\n+        var left = this.mixer_registers[address_new_left] & 0xF0;\n+        var right = this.mixer_registers[address_new_right] >>> 4;\n+        return left | right;\n+    };\n+\n+    /** @this {SB16} */\n+    MIXER_WRITE_HANDLERS[address_old] = function(data)\n+    {\n+        this.mixer_registers[address_old] = data;\n+        var prev_left = this.mixer_registers[address_new_left];\n+        var prev_right = this.mixer_registers[address_new_right];\n+        var left = (data & 0xF0) | (prev_left & 0x0F);\n+        var right = (data << 4 & 0xF0) | (prev_right & 0x0F);\n+\n+        this.mixer_write(address_new_left, left);\n+        this.mixer_write(address_new_right, right);\n+    };\n+}\n+\n+function register_mixer_volume(address, mixer_source, channel)\n+{\n+    MIXER_READ_HANDLERS[address] = SB16.prototype.mixer_default_read;\n+\n+    /** @this {SB16} */\n+    MIXER_WRITE_HANDLERS[address] = function(data)\n+    {\n+        this.mixer_registers[address] = data;\n+        this.bus.send(\"mixer-volume\",\n+        [\n+            mixer_source,\n+            channel,\n+            (data >>> 2) - 62\n+        ]);\n+    };\n+};\n+\n // Reset.\n register_mixer_read(0x00, function()\n {\n+    this.mixer_reset();\n     return 0;\n });\n register_mixer_write(0x00);\n \n-// Output Stereo Select.\n-register_mixer_write(0x0E, function(bits)\n+// Legacy Voice Volume Left/Right.\n+register_mixer_legacy(0x04, 0x32, 0x33);\n+\n+// Legacy Mic Volume. TODO.\n+//register_mixer_read(0x0A);\n+//register_mixer_write(0x0A, function(data)\n+//{\n+//    this.mixer_registers[0x0A] = data;\n+//    var prev = this.mixer_registers[0x3A];\n+//    this.mixer_write(0x3A, data << 5 | (prev & 0x0F));\n+//});\n+\n+// Legacy Master Volume Left/Right.\n+register_mixer_legacy(0x22, 0x30, 0x31);\n+// Legacy Midi Volume Left/Right.\n+register_mixer_legacy(0x26, 0x34, 0x35);\n+// Legacy CD Volume Left/Right.\n+register_mixer_legacy(0x28, 0x36, 0x37);\n+// Legacy Line Volume Left/Right.\n+register_mixer_legacy(0x2E, 0x38, 0x39);\n+\n+// Master Volume Left.\n+register_mixer_volume(0x30, \"master\", \"left\");\n+// Master Volume Right.\n+register_mixer_volume(0x31, \"master\", \"right\");\n+// Voice Volume Left.\n+register_mixer_volume(0x32, \"dac\", \"left\");\n+// Voice Volume Right.\n+register_mixer_volume(0x33, \"dac\", \"right\");\n+// MIDI Volume Left. TODO.\n+//register_mixer_volume(0x34, \"synth\", \"left\");\n+// MIDI Volume Right. TODO.\n+//register_mixer_volume(0x35, \"synth\", \"right);\n+// CD Volume Left. TODO.\n+//register_mixer_volume(0x36, \"cd\", \"left);\n+// CD Volume Right. TODO.\n+//register_mixer_volume(0x37, \"cd\", \"right);\n+// Line Volume Left. TODO.\n+//register_mixer_volume(0x38, \"line\", \"left\");\n+// Line Volume Right. TODO.\n+//register_mixer_volume(0x39, \"line\", \"right\");\n+// Mic Volume. TODO.\n+//register_mixer_volume(0x3A, \"mic\", \"both\");\n+\n+// PC Speaker Volume.\n+register_mixer_read(0x3B);\n+register_mixer_write(0x3B, function(data)\n+{\n+    this.mixer_registers[0x3B] = data;\n+    this.bus.send(\"mixer-volume\", [\"pcspeaker\", \"both\", (data >>> 6) * 6 - 18]);\n+});\n+\n+// Output Mixer Switches. TODO.\n+//register_mixer_read(0x3C);\n+//register_mixer_write(0x3C, function(data)\n+//{\n+//    this.mixer_registers[0x3C] = data;\n+//\n+//    if(data & 0x01) this.bus.send(\"mixer-connect\", [\"mic\", \"both\"]);\n+//    else this.bus.send(\"mixer-disconnect\", [\"mic\", \"both\"]);\n+//\n+//    if(data & 0x02) this.bus.send(\"mixer-connect\", [\"cd\", \"right\"]);\n+//    else this.bus.send(\"mixer-disconnect\", [\"cd\", \"right\"]);\n+//\n+//    if(data & 0x04) this.bus.send(\"mixer-connect\", [\"cd\", \"left\"]);\n+//    else this.bus.send(\"mixer-disconnect\", [\"cd\", \"left\"]);\n+//\n+//    if(data & 0x08) this.bus.send(\"mixer-connect\", [\"line\", \"right\"]);\n+//    else this.bus.send(\"mixer-disconnect\", [\"line\", \"right\"]);\n+//\n+//    if(data & 0x10) this.bus.send(\"mixer-connect\", [\"line\", \"left\"]);\n+//    else this.bus.send(\"mixer-disconnect\", [\"line\", \"left\"]);\n+//});\n+\n+// Input Mixer Left Switches. TODO.\n+//register_mixer_read(0x3D);\n+//register_mixer_write(0x3D);\n+\n+// Input Mixer Right Switches. TODO.\n+//register_mixer_read(0x3E);\n+//register_mixer_write(0x3E);\n+\n+// Input Gain Left. TODO.\n+//register_mixer_read(0x3F);\n+//register_mixer_write(0x3F);\n+\n+// Input Gain Right. TODO.\n+//register_mixer_read(0x40);\n+//register_mixer_write(0x40);\n+\n+// Output Gain Left.\n+register_mixer_read(0x41);\n+register_mixer_write(0x41, function(data)\n+{\n+    this.mixer_registers[0x41] = data;\n+    this.bus.send(\"mixer-gain-left\", (data >>> 6) * 6);\n+});\n+\n+// Output Gain Right.\n+register_mixer_read(0x42);\n+register_mixer_write(0x42, function(data)\n+{\n+    this.mixer_registers[0x42] = data;\n+    this.bus.send(\"mixer-gain-right\", (data >>> 6) * 6);\n+});\n+\n+// Mic AGC. TODO.\n+//register_mixer_read(0x43);\n+//register_mixer_write(0x43);\n+\n+// Treble Left.\n+register_mixer_read(0x44);\n+register_mixer_write(0x44, function(data)\n+{\n+    this.mixer_registers[0x44] = data;\n+    this.bus.send(\"mixer-treble-left\", (data >>> 3) - (data >>> 7) - 14);","path":"src/sb16.js","position":554,"original_position":554,"commit_id":"78c960870212f4f0696130c084b532a653212bd7","original_commit_id":"78c960870212f4f0696130c084b532a653212bd7","user":{"login":"ErnWong","id":2609018,"avatar_url":"https://avatars2.githubusercontent.com/u/2609018?v=4","gravatar_id":"","url":"https://api.github.com/users/ErnWong","html_url":"https://github.com/ErnWong","followers_url":"https://api.github.com/users/ErnWong/followers","following_url":"https://api.github.com/users/ErnWong/following{/other_user}","gists_url":"https://api.github.com/users/ErnWong/gists{/gist_id}","starred_url":"https://api.github.com/users/ErnWong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ErnWong/subscriptions","organizations_url":"https://api.github.com/users/ErnWong/orgs","repos_url":"https://api.github.com/users/ErnWong/repos","events_url":"https://api.github.com/users/ErnWong/events{/privacy}","received_events_url":"https://api.github.com/users/ErnWong/received_events","type":"User","site_admin":false},"body":"Ah this one's interesting. The manual says that the higher 4 bits are treated as a number, and this number's value gets mapped in the following way:\r\n\r\n- Values 0 to 7 --> levels -14 to 0 dB in steps of 2 dB.\r\n- Values from 8 to 15 --> levels 0 to 14 dB in steps of 2 dB. \r\n\r\nThe middle two values, 7 and 8, both gets mapped to 0 dB. This elegant/hackish code does this using `data >>> 7`, which offsets the dB level by -2 dB when bit 7 is set.\r\n\r\nDo you reckon I should make this clearer?","created_at":"2018-03-17T08:12:10Z","updated_at":"2018-03-17T08:12:10Z","html_url":"https://github.com/copy/v86/pull/199#discussion_r175251449","pull_request_url":"https://api.github.com/repos/copy/v86/pulls/199","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/copy/v86/pulls/comments/175251449"},"html":{"href":"https://github.com/copy/v86/pull/199#discussion_r175251449"},"pull_request":{"href":"https://api.github.com/repos/copy/v86/pulls/199"}},"in_reply_to_id":175243203},"pull_request":{"url":"https://api.github.com/repos/copy/v86/pulls/199","id":174835290,"html_url":"https://github.com/copy/v86/pull/199","diff_url":"https://github.com/copy/v86/pull/199.diff","patch_url":"https://github.com/copy/v86/pull/199.patch","issue_url":"https://api.github.com/repos/copy/v86/issues/199","number":199,"state":"open","locked":false,"title":"Implement SB16 Mixer Chip + Several Speaker Improvements","user":{"login":"ErnWong","id":2609018,"avatar_url":"https://avatars2.githubusercontent.com/u/2609018?v=4","gravatar_id":"","url":"https://api.github.com/users/ErnWong","html_url":"https://github.com/ErnWong","followers_url":"https://api.github.com/users/ErnWong/followers","following_url":"https://api.github.com/users/ErnWong/following{/other_user}","gists_url":"https://api.github.com/users/ErnWong/gists{/gist_id}","starred_url":"https://api.github.com/users/ErnWong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ErnWong/subscriptions","organizations_url":"https://api.github.com/users/ErnWong/orgs","repos_url":"https://api.github.com/users/ErnWong/repos","events_url":"https://api.github.com/users/ErnWong/events{/privacy}","received_events_url":"https://api.github.com/users/ErnWong/received_events","type":"User","site_admin":false},"body":"Good grief. I made another ginormous PR.\r\n\r\n### The Good Parts™\r\n\r\n- Implements the mixer chip.\r\n- Fixes several SB16 bugs.\r\n- Refactors Speaker Adapter into smaller components:\r\n   - PCSpeaker - *responsible for the beeping sounds and [really awesome retro game music](https://drive.google.com/open?id=1ErJTdOOKkfNHhk4_Sq2gLosfcn1P4C5I)*.\r\n   - DAC - *responsible for queuing and continuously playing audio data - such as Windows 98 sounds and Doom sound effects*.\r\n   - Mixer - *responsible for mixing, panning, and boosting the audio from PCSpeaker and DAC*.\r\n\r\n   - The mixer amplifies audio aliases from my naive sample rate converter (which sounds horrible), so I decided to fix the sample rate conversion using the following...\r\n\r\n- Removes the deprecated `ScriptProcessorNode`-based DAC, and\r\n- Reimplements the DAC component using `AudioBuffers` inspired by [this article](http://blog.mecheye.net/2017/09/i-dont-know-who-the-web-audio-api-is-designed-for/) by [Jasper St. Pierre](https://github.com/magcius). Fixes the following audio glitches:\r\n   - Sometimes sounds with a short duration doesn't get played.\r\n   - Sometimes an audio playback does not finish completely, and reappears in the beginning of the next audio playback.\r\n\r\n- Using `AudioBuffers` allows us to use the browser's sample rate converter, so audio playback is now in-tune! Listen to [the Windows 98 startup sound comparisons](https://drive.google.com/open?id=13K08FdPGrE4xncbWCRZVc6orKmajhKNe). You'll notice that it's *much* better.\r\n\r\n- Simplifies the two audio queuing loops into one queuing loop. This helps reducing latency as well.\r\n```\r\nPreviously:\r\nSB16 creates a reserve of audio data received from the DMA, and\r\nSpeaker creates a reserve audio data received from SB16\r\n _____         ______         _________\r\n|     | .->-. |      | .->-. |         |\r\n| DMA | |   | | SB16 | |   | | Speaker |\r\n|_____| '-<-' |______| '-<-' |_________|\r\n\r\nNow:\r\nOnly Speaker creates a reserve, and audio gets passed down the\r\npipeline almost instantly.\r\n _____         ______         _________\r\n|     | .->---|      |--->-. |         |\r\n| DMA | |     | SB16 |     | | Speaker |\r\n|_____| '-<---|______|---<-' |_________|\r\n\r\n```\r\n\r\n- Implements a second DAC that uses Audio Worklets instead of Audio Buffers.\r\n   - Audio Worklets is a new and exciting piece of API.\r\n   - So far, only Chrome since Chrome 64 has implemented `AudioWorklets`, [under some flags:](https://developers.google.com/web/updates/2017/12/audio-worklet) \r\n`chrome --enable-blink-features=Worklet,AudioWorklet`\r\n   - My worklet-based DAC implements a simple Lanczos Resampler to convert between sample rates.\r\n   - Worklet allows latency to be reduced further.\r\n\r\n### The Bad Parts\r\n\r\n- In Firefox, the *buffer*-based DAC works well, but in Chrome, it suffers from a fair amount of pops and clicks.\r\n   - The good news is that the *worklet*-based DAC eliminates all pops and clicks, and Chrome is the first to implement Audio Worklets. The catch is that Audio Worklets are not ready for public yet. I'll keep an eye on their progress.\r\n   - Demo for [Chrome buffer-based](https://drive.google.com/open?id=1HqnvRrbY12GHuyFO03fSKVPbY9wtxmqm) (caution: irritating clicks),\r\n   - Demo for [Firefox buffer-based](https://drive.google.com/open?id=1Me9pi3PzHz4Rohrgd7c1gRFYQTcgr4x0), and\r\n   - Demo for [Chrome worklet-based](https://drive.google.com/open?id=12v3oH4fhMzDncZkXRMYSSewNio7azaAh)\r\n\r\n- The buffer-based DAC allocates a new `AudioBuffer` and a new `AudioBufferSourceNode` for every block, and a new block is processed approximately every 2~20ms. These new objects eventually gets garbage collected. Allocation and GC in this hot loop doesn't *feel* right.\r\n\r\n   - I went to see if keeping a pool of `AudioBuffers` was worth it, as it will reduce the amount of GC. Since the buffer size can vary, I've put them in a `Map` of arrays of `AudioBuffers` that get populated with new `AudioBuffers` on demand.\r\n   - After some quick performance and memory profiling, keeping a pool of `AudioBuffers` doesn't seem to show any significant improvements, so I went back to creating new `AudioBuffers` each block.\r\n   - Now I think about it, I might have profiled it incorrectly, and I might have chose a bad test situation for it.\r\n\r\n- I'm not too sure where I should put the Audio Worklet code. The Audio Worklet is a bit like Web Workers in that you register them by supplying a URL of the worklet code to an API function, and the code gets executed in another thread (in this case, the Audio Rendering Thread).\r\n   - At the moment, the worklet code lives inside an ES6 template string that gets converted into a `Blob` and from a `Blob` into an `ObjectURL`. This means that the worklet code doesn't get seen by the compiler. The worklet code can't be compiled because the Closure Compiler doesn't support outputting ES6 classes yet, which is needed to use the Audio Worklet APIs.\r\n\r\nAs always, I'm happy for any feedback, and I'm happy to rewrite any part of this PR. I also don't mind if you take a while to review this.\r\n\r\nI think the state restore in this PR should be compatible with the state files before this PR. The only change is that some old states are no longer used and are ignored. However, new state files might confuse older v86. Should I increment the state version?","created_at":"2018-03-14T01:55:52Z","updated_at":"2018-03-17T08:12:10Z","closed_at":null,"merged_at":null,"merge_commit_sha":"2caf1a2b84455f8eec319cb3e3803b8979154fd2","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/copy/v86/pulls/199/commits","review_comments_url":"https://api.github.com/repos/copy/v86/pulls/199/comments","review_comment_url":"https://api.github.com/repos/copy/v86/pulls/comments{/number}","comments_url":"https://api.github.com/repos/copy/v86/issues/199/comments","statuses_url":"https://api.github.com/repos/copy/v86/statuses/78c960870212f4f0696130c084b532a653212bd7","head":{"label":"ErnWong:dev-speaker","ref":"dev-speaker","sha":"78c960870212f4f0696130c084b532a653212bd7","user":{"login":"ErnWong","id":2609018,"avatar_url":"https://avatars2.githubusercontent.com/u/2609018?v=4","gravatar_id":"","url":"https://api.github.com/users/ErnWong","html_url":"https://github.com/ErnWong","followers_url":"https://api.github.com/users/ErnWong/followers","following_url":"https://api.github.com/users/ErnWong/following{/other_user}","gists_url":"https://api.github.com/users/ErnWong/gists{/gist_id}","starred_url":"https://api.github.com/users/ErnWong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ErnWong/subscriptions","organizations_url":"https://api.github.com/users/ErnWong/orgs","repos_url":"https://api.github.com/users/ErnWong/repos","events_url":"https://api.github.com/users/ErnWong/events{/privacy}","received_events_url":"https://api.github.com/users/ErnWong/received_events","type":"User","site_admin":false},"repo":{"id":110044060,"name":"v86","full_name":"ErnWong/v86","owner":{"login":"ErnWong","id":2609018,"avatar_url":"https://avatars2.githubusercontent.com/u/2609018?v=4","gravatar_id":"","url":"https://api.github.com/users/ErnWong","html_url":"https://github.com/ErnWong","followers_url":"https://api.github.com/users/ErnWong/followers","following_url":"https://api.github.com/users/ErnWong/following{/other_user}","gists_url":"https://api.github.com/users/ErnWong/gists{/gist_id}","starred_url":"https://api.github.com/users/ErnWong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ErnWong/subscriptions","organizations_url":"https://api.github.com/users/ErnWong/orgs","repos_url":"https://api.github.com/users/ErnWong/repos","events_url":"https://api.github.com/users/ErnWong/events{/privacy}","received_events_url":"https://api.github.com/users/ErnWong/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/ErnWong/v86","description":"x86 virtualization in JavaScript, running in your browser and NodeJS","fork":true,"url":"https://api.github.com/repos/ErnWong/v86","forks_url":"https://api.github.com/repos/ErnWong/v86/forks","keys_url":"https://api.github.com/repos/ErnWong/v86/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ErnWong/v86/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ErnWong/v86/teams","hooks_url":"https://api.github.com/repos/ErnWong/v86/hooks","issue_events_url":"https://api.github.com/repos/ErnWong/v86/issues/events{/number}","events_url":"https://api.github.com/repos/ErnWong/v86/events","assignees_url":"https://api.github.com/repos/ErnWong/v86/assignees{/user}","branches_url":"https://api.github.com/repos/ErnWong/v86/branches{/branch}","tags_url":"https://api.github.com/repos/ErnWong/v86/tags","blobs_url":"https://api.github.com/repos/ErnWong/v86/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ErnWong/v86/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ErnWong/v86/git/refs{/sha}","trees_url":"https://api.github.com/repos/ErnWong/v86/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ErnWong/v86/statuses/{sha}","languages_url":"https://api.github.com/repos/ErnWong/v86/languages","stargazers_url":"https://api.github.com/repos/ErnWong/v86/stargazers","contributors_url":"https://api.github.com/repos/ErnWong/v86/contributors","subscribers_url":"https://api.github.com/repos/ErnWong/v86/subscribers","subscription_url":"https://api.github.com/repos/ErnWong/v86/subscription","commits_url":"https://api.github.com/repos/ErnWong/v86/commits{/sha}","git_commits_url":"https://api.github.com/repos/ErnWong/v86/git/commits{/sha}","comments_url":"https://api.github.com/repos/ErnWong/v86/comments{/number}","issue_comment_url":"https://api.github.com/repos/ErnWong/v86/issues/comments{/number}","contents_url":"https://api.github.com/repos/ErnWong/v86/contents/{+path}","compare_url":"https://api.github.com/repos/ErnWong/v86/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ErnWong/v86/merges","archive_url":"https://api.github.com/repos/ErnWong/v86/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ErnWong/v86/downloads","issues_url":"https://api.github.com/repos/ErnWong/v86/issues{/number}","pulls_url":"https://api.github.com/repos/ErnWong/v86/pulls{/number}","milestones_url":"https://api.github.com/repos/ErnWong/v86/milestones{/number}","notifications_url":"https://api.github.com/repos/ErnWong/v86/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ErnWong/v86/labels{/name}","releases_url":"https://api.github.com/repos/ErnWong/v86/releases{/id}","deployments_url":"https://api.github.com/repos/ErnWong/v86/deployments","created_at":"2017-11-08T23:52:32Z","updated_at":"2018-01-15T05:48:39Z","pushed_at":"2018-03-14T00:34:45Z","git_url":"git://github.com/ErnWong/v86.git","ssh_url":"git@github.com:ErnWong/v86.git","clone_url":"https://github.com/ErnWong/v86.git","svn_url":"https://github.com/ErnWong/v86","homepage":null,"size":10326,"stargazers_count":2,"watchers_count":2,"language":"JavaScript","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":0,"open_issues":0,"watchers":2,"default_branch":"master"}},"base":{"label":"copy:master","ref":"master","sha":"4447a52e14a04281c8d397a8e8ab337c8f49233f","user":{"login":"copy","id":1816456,"avatar_url":"https://avatars3.githubusercontent.com/u/1816456?v=4","gravatar_id":"","url":"https://api.github.com/users/copy","html_url":"https://github.com/copy","followers_url":"https://api.github.com/users/copy/followers","following_url":"https://api.github.com/users/copy/following{/other_user}","gists_url":"https://api.github.com/users/copy/gists{/gist_id}","starred_url":"https://api.github.com/users/copy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/copy/subscriptions","organizations_url":"https://api.github.com/users/copy/orgs","repos_url":"https://api.github.com/users/copy/repos","events_url":"https://api.github.com/users/copy/events{/privacy}","received_events_url":"https://api.github.com/users/copy/received_events","type":"User","site_admin":false},"repo":{"id":14158237,"name":"v86","full_name":"copy/v86","owner":{"login":"copy","id":1816456,"avatar_url":"https://avatars3.githubusercontent.com/u/1816456?v=4","gravatar_id":"","url":"https://api.github.com/users/copy","html_url":"https://github.com/copy","followers_url":"https://api.github.com/users/copy/followers","following_url":"https://api.github.com/users/copy/following{/other_user}","gists_url":"https://api.github.com/users/copy/gists{/gist_id}","starred_url":"https://api.github.com/users/copy/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/copy/subscriptions","organizations_url":"https://api.github.com/users/copy/orgs","repos_url":"https://api.github.com/users/copy/repos","events_url":"https://api.github.com/users/copy/events{/privacy}","received_events_url":"https://api.github.com/users/copy/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/copy/v86","description":"x86 virtualization in JavaScript, running in your browser and NodeJS","fork":false,"url":"https://api.github.com/repos/copy/v86","forks_url":"https://api.github.com/repos/copy/v86/forks","keys_url":"https://api.github.com/repos/copy/v86/keys{/key_id}","collaborators_url":"https://api.github.com/repos/copy/v86/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/copy/v86/teams","hooks_url":"https://api.github.com/repos/copy/v86/hooks","issue_events_url":"https://api.github.com/repos/copy/v86/issues/events{/number}","events_url":"https://api.github.com/repos/copy/v86/events","assignees_url":"https://api.github.com/repos/copy/v86/assignees{/user}","branches_url":"https://api.github.com/repos/copy/v86/branches{/branch}","tags_url":"https://api.github.com/repos/copy/v86/tags","blobs_url":"https://api.github.com/repos/copy/v86/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/copy/v86/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/copy/v86/git/refs{/sha}","trees_url":"https://api.github.com/repos/copy/v86/git/trees{/sha}","statuses_url":"https://api.github.com/repos/copy/v86/statuses/{sha}","languages_url":"https://api.github.com/repos/copy/v86/languages","stargazers_url":"https://api.github.com/repos/copy/v86/stargazers","contributors_url":"https://api.github.com/repos/copy/v86/contributors","subscribers_url":"https://api.github.com/repos/copy/v86/subscribers","subscription_url":"https://api.github.com/repos/copy/v86/subscription","commits_url":"https://api.github.com/repos/copy/v86/commits{/sha}","git_commits_url":"https://api.github.com/repos/copy/v86/git/commits{/sha}","comments_url":"https://api.github.com/repos/copy/v86/comments{/number}","issue_comment_url":"https://api.github.com/repos/copy/v86/issues/comments{/number}","contents_url":"https://api.github.com/repos/copy/v86/contents/{+path}","compare_url":"https://api.github.com/repos/copy/v86/compare/{base}...{head}","merges_url":"https://api.github.com/repos/copy/v86/merges","archive_url":"https://api.github.com/repos/copy/v86/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/copy/v86/downloads","issues_url":"https://api.github.com/repos/copy/v86/issues{/number}","pulls_url":"https://api.github.com/repos/copy/v86/pulls{/number}","milestones_url":"https://api.github.com/repos/copy/v86/milestones{/number}","notifications_url":"https://api.github.com/repos/copy/v86/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/copy/v86/labels{/name}","releases_url":"https://api.github.com/repos/copy/v86/releases{/id}","deployments_url":"https://api.github.com/repos/copy/v86/deployments","created_at":"2013-11-06T00:12:12Z","updated_at":"2018-03-17T03:59:59Z","pushed_at":"2018-03-14T01:55:52Z","git_url":"git://github.com/copy/v86.git","ssh_url":"git@github.com:copy/v86.git","clone_url":"https://github.com/copy/v86.git","svn_url":"https://github.com/copy/v86","homepage":null,"size":10157,"stargazers_count":4282,"watchers_count":4282,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":400,"mirror_url":null,"archived":false,"open_issues_count":26,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":400,"open_issues":26,"watchers":4282,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/copy/v86/pulls/199"},"html":{"href":"https://github.com/copy/v86/pull/199"},"issue":{"href":"https://api.github.com/repos/copy/v86/issues/199"},"comments":{"href":"https://api.github.com/repos/copy/v86/issues/199/comments"},"review_comments":{"href":"https://api.github.com/repos/copy/v86/pulls/199/comments"},"review_comment":{"href":"https://api.github.com/repos/copy/v86/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/copy/v86/pulls/199/commits"},"statuses":{"href":"https://api.github.com/repos/copy/v86/statuses/78c960870212f4f0696130c084b532a653212bd7"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"ErnWong"}}