Git Product home page Git Product logo

Comments (14)

Wouter1 avatar Wouter1 commented on August 23, 2024

added exact byte of wrap to the output.
Add this code just before push in GatherInputSamples

            UInt32 remaining = usbRing->size - usbRing->writehead;
            if (size >= remaining  ) {
                debugIOLog("input wrap in list %d at frame %d byte %ld",currentFrameList,frameIndex, remaining);
            }

and this in EMUUSBOutputStrema as first statement in if(thisFrameSize >= nuBytesToBufferEnd):

            debugIOLog("write wrap in usbframe %lld list %d byte %d",nextUsableUsbFrameNr,n,numBytesToBufferEnd );

Here is short part of the output (48kHz rate)
note, the 492 numbers indicate a matching pair of input/output wraps, notice that the input wrap in list 2 referred to can be found a bit higher in the list where the corresponding read was started.

Jan 12 21:50:59 vlieland kernel[0]: READ framelist 3 in framenr 41129284 at 42843674137372
Jan 12 21:50:59 vlieland kernel[0]: READ framelist 0 in framenr 41129348 at 42843738130412
Jan 12 21:50:59 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 276
Jan 12 21:50:59 vlieland kernel[0]: READ framelist 1 in framenr 41129412 at 42843802140518
Jan 12 21:50:59 vlieland kernel[0]: write wrap in usbframe 41129348 list 0 byte 84
Jan 12 21:50:59 vlieland kernel[0]: READ framelist 2 in framenr 41129476 at 42843866128146
Jan 12 21:50:59 vlieland kernel[0]: READ framelist 3 in framenr 41129540 at 42843930130317
Jan 12 21:50:59 vlieland kernel[0]: input wrap in list 0 at frame 0 byte 108   <------ difference 24 ------
Jan 12 21:51:00 vlieland kernel[0]: write wrap in usbframe 41129476 list 42 byte **492**
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 0 in framenr 41129604 at 42843994143594
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 1 in framenr 41129668 at 42844058125664
Jan 12 21:51:00 vlieland kernel[0]: input wrap in list 2 at frame 42 byte **492** (41129476)
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 2 in framenr 41129732 at 42844122129566
Jan 12 21:51:00 vlieland kernel[0]: write wrap in usbframe 41129668 list 21 byte 300
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 3 in framenr 41129796 at 42844186139679
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 0 in framenr 41129860 at 42844250128222
Jan 12 21:51:00 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 300
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 1 in framenr 41129924 at 42844314133596
Jan 12 21:51:00 vlieland kernel[0]: write wrap in usbframe 41129860 list 0 byte 108
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 2 in framenr 41129988 at 42844378123573
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 3 in framenr 41130052 at 42844442138839
Jan 12 21:51:00 vlieland kernel[0]: input wrap in list 0 at frame 0 byte 108
Jan 12 21:51:00 vlieland kernel[0]: write wrap in usbframe 41129988 list 42 byte 492
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 0 in framenr 41130116 at 42844506100599
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 1 in framenr 41130180 at 42844570125000
Jan 12 21:51:00 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 492
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 2 in framenr 41130244 at 42844634133223
Jan 12 21:51:00 vlieland kernel[0]: write wrap in usbframe 41130180 list 21 byte 300
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 3 in framenr 41130308 at 42844698137361
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 0 in framenr 41130372 at 42844762092630
Jan 12 21:51:00 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 300
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 1 in framenr 41130436 at 42844826127069
Jan 12 21:51:00 vlieland kernel[0]: write wrap in usbframe 41130372 list 0 byte 108
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 2 in framenr 41130500 at 42844890138024
Jan 12 21:51:00 vlieland kernel[0]: READ framelist 3 in framenr 41130564 at 42844954127344
Jan 12 21:51:00 vlieland kernel[0]: input wrap in list 0 at frame 0 byte 108
Jan 12 21:51:01 vlieland kernel[0]: write wrap in usbframe 41130500 list 42 byte 492
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 0 in framenr 41130628 at 42845018140271
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 1 in framenr 41130692 at 42845082114698
Jan 12 21:51:01 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 492
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 2 in framenr 41130756 at 42845146133263
Jan 12 21:51:01 vlieland kernel[0]: write wrap in usbframe 41130692 list 21 byte 300
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 3 in framenr 41130820 at 42845210022809
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 0 in framenr 41130884 at 42845274129761
Jan 12 21:51:01 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 312 <---- diffference 12 ----
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 1 in framenr 41130948 at 42845338130733
Jan 12 21:51:01 vlieland kernel[0]: write wrap in usbframe 41130884 list 0 byte 120
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 2 in framenr 41131012 at 42845402134576
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 3 in framenr 41131076 at 42845466132018
Jan 12 21:51:01 vlieland kernel[0]: input wrap in list 0 at frame 0 byte 120
Jan 12 21:51:01 vlieland kernel[0]: write wrap in usbframe 41131012 list 42 byte 504
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 0 in framenr 41131140 at 42845530144123
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 1 in framenr 41131204 at 42845594083202
Jan 12 21:51:01 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 504
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 2 in framenr 41131268 at 42845658134839
Jan 12 21:51:01 vlieland kernel[0]: write wrap in usbframe 41131204 list 21 byte 312
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 3 in framenr 41131332 at 42845722126040
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 0 in framenr 41131396 at 42845786129646
Jan 12 21:51:01 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 312
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 1 in framenr 41131460 at 42845850131380
Jan 12 21:51:01 vlieland kernel[0]: write wrap in usbframe 41131396 list 0 byte 120
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 2 in framenr 41131524 at 42845914101124
Jan 12 21:51:01 vlieland kernel[0]: READ framelist 3 in framenr 41131588 at 42845978102680
Jan 12 21:51:01 vlieland kernel[0]: input wrap in list 0 at frame 0 byte 120
Jan 12 21:51:02 vlieland kernel[0]: write wrap in usbframe 41131524 list 42 byte 504
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 0 in framenr 41131652 at 42846042140811
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 1 in framenr 41131716 at 42846106130473
Jan 12 21:51:02 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 528   <----- difference 24 ---
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 2 in framenr 41131780 at 42846170133244
Jan 12 21:51:02 vlieland kernel[0]: write wrap in usbframe 41131716 list 21 byte 336
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 3 in framenr 41131844 at 42846234137013
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 0 in framenr 41131908 at 42846298128973
Jan 12 21:51:02 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 336
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 1 in framenr 41131972 at 42846362130821
Jan 12 21:51:02 vlieland kernel[0]: write wrap in usbframe 41131908 list 0 byte 144
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 2 in framenr 41132036 at 42846426137975
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 3 in framenr 41132100 at 42846490132361
Jan 12 21:51:02 vlieland kernel[0]: input wrap in list 0 at frame 0 byte 144
Jan 12 21:51:02 vlieland kernel[0]: write wrap in usbframe 41132036 list 42 byte 528
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 0 in framenr 41132164 at 42846554139515
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 1 in framenr 41132228 at 42846618128039
Jan 12 21:51:02 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 528
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 2 in framenr 41132292 at 42846682127653
Jan 12 21:51:02 vlieland kernel[0]: write wrap in usbframe 41132228 list 21 byte 336
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 3 in framenr 41132356 at 42846746101697
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 0 in framenr 41132420 at 42846810128819
Jan 12 21:51:02 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 336
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 1 in framenr 41132484 at 42846874137719
Jan 12 21:51:02 vlieland kernel[0]: write wrap in usbframe 41132420 list 0 byte 144
Jan 12 21:51:02 vlieland kernel[0]: READ framelist 2 in framenr 41132548 at 42846938139866
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 3 in framenr 41132612 at 42847002129127
Jan 12 21:51:03 vlieland kernel[0]: input wrap in list 0 at frame 0 byte 144
Jan 12 21:51:03 vlieland kernel[0]: write wrap in usbframe 41132548 list 42 byte 528
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 0 in framenr 41132676 at 42847066144448
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 1 in framenr 41132740 at 42847130130412
Jan 12 21:51:03 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 552   <------ 24 ------
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 2 in framenr 41132804 at 42847194133757
Jan 12 21:51:03 vlieland kernel[0]: write wrap in usbframe 41132740 list 21 byte 360
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 3 in framenr 41132868 at 42847258101562
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 0 in framenr 41132932 at 42847322089354
Jan 12 21:51:03 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 360
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 1 in framenr 41132996 at 42847386121500
Jan 12 21:51:03 vlieland kernel[0]: write wrap in usbframe 41132932 list 0 byte 168
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 2 in framenr 41133060 at 42847450149622
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 3 in framenr 41133124 at 42847514128496
Jan 12 21:51:03 vlieland kernel[0]: input wrap in list 0 at frame 0 byte 168
Jan 12 21:51:03 vlieland kernel[0]: write wrap in usbframe 41133060 list 42 byte 552
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 0 in framenr 41133188 at 42847578117311
Jan 12 21:51:03 vlieland kernel[0]: READ framelist 1 in framenr 41133252 at 42847642129078
Jan 12 21:51:03 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 552

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

So the input and output are EXACTLY synchronized. Except for occasional offsets of 12 or 24 bytes.

I will use simple script to extract the differences (#bytes).

cat /Users/wouter/Desktop/96k.txt | awk '// { if ($6=="write") { L=$12; B1=$14; } else if ($6=="input" && L==$13) { B2=$15; print "wrap diff " B1-B2 } }'

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

Here is a plot of B1-B2 for 96kHz
untitled-1

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

And here the same plot for 48kHz
48

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

Note that for 48kHz we have 4 samples per frame so we have jumps of 4*3=12 bytes. The range and behiour seems similar: 1, 2, or 3 stereo(quad) samples off.

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

For 44kHz the picture is a bit worse

44

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

For 44k the pattern is similar but we are not at 0 most of the time but at 228. And we have offsets in 2 directions now.

I now realize the other pictures are probably also symmetric but I am plotting only 1 side because it's so close to 0 and my script has the requriement L==$13 meaning the framenrs have to match. So the odd data has been dropped in the pictures where the center is around 0.

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

The offset at 44.1 must be caused by initial 3 cycles where the write list has to guess the frame sizes, waiting for the reads to come in

You cxan also see that happen in the logs , in "frameSizeQueue empty, guessing some queue size. May need fix..
":

Jan 12 22:15:01 vlieland kernel[0]: +UsbInputRing::init bytesize=98304 byterate=529200
Jan 12 22:15:01 vlieland kernel[0]: -UsbInputRing::init 185759637
Jan 12 22:15:01 vlieland kernel[0]: create output pipe 
Jan 12 22:15:01 vlieland kernel[0]: check for associated endpoint
Jan 12 22:15:01 vlieland kernel[0]: READ framelist 0 in framenr 42570526 at 44285045041467
Jan 12 22:15:01 vlieland kernel[0]: READ framelist 1 in framenr 42570590 at 44285045117215
Jan 12 22:15:01 vlieland kernel[0]: READ framelist 2 in framenr 42570654 at 44285045176191
Jan 12 22:15:01 vlieland kernel[0]: READ framelist 3 in framenr 42570718 at 44285045231161
Jan 12 22:15:01 vlieland kernel[0]: frameSizeQueue empty, guessing some queue size. May need fix..
Jan 12 22:15:01 --- last message repeated 127 times ---
Jan 12 22:15:01 vlieland kernel[0]: ++EMUUSBAudioEngine[0xffffff8034361800]::performEngineStart result is 0x0 direction 1
Jan 12 22:15:01 vlieland kernel[0]: USBF:    44285.108    AppleUSBXHCI[0xffffff81a21d5000]::PollEventRing2 - Isoc Transfer event with completion code (3) on pEP (0xffffff803579c800)
Jan 12 22:15:01 vlieland kernel[0]: USBF:    44285.109    AppleUSBXHCI[0xffffff81a21d5000]::PollEventRing2 - Isoc Transfer event with completion code (3) on pEP (0xffffff803579c800)
Jan 12 22:15:01 vlieland kernel[0]: frameSizeQueue empty, guessing some queue size. May need fix..
Jan 12 22:15:01 --- last message repeated 58 times ---
Jan 12 22:15:01 vlieland kernel[0]: write wrap in usbframe 42570654 list 58 byte 96
Jan 12 22:15:01 vlieland kernel[0]: frameSizeQueue empty, guessing some queue size. May need fix..
Jan 12 22:15:01 --- last message repeated 4 times ---
Jan 12 22:15:01 vlieland kernel[0]: READ framelist 0 in framenr 42570782 at 44285172276763
Jan 12 22:15:01 vlieland kernel[0]: READ framelist 1 in framenr 42570846 at 44285236119008

3 cycles = 3*64 = 192 samples. At 44.1kHz,we guess 44 so we are 1 off every 10, or 19 samples off in total.

19 quad samples = 228 bytes, exactly the amount we found. So we can compensate for this error.

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

At 192kHz the sync is not working ok it seems. Here a bit


Jan 12 22:35:09 vlieland kernel[0]: write wrap in usbframe 43779026 list 42 byte 366
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 1 in framenr 43779090 at 45493576453671
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 2 in framenr 43779122 at 45493608509714
Jan 12 22:35:09 vlieland kernel[0]: input wrap in list 3 at frame 63 byte 558
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 3 in framenr 43779154 at 45493640558442
Jan 12 22:35:09 vlieland kernel[0]: write wrap in usbframe 43779122 list 21 byte 174
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 0 in framenr 43779186 at 45493672544412
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 1 in framenr 43779218 at 45493704556480
Jan 12 22:35:09 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 366
Jan 12 22:35:09 vlieland kernel[0]: write wrap in usbframe 43779186 list 63 byte 558
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 2 in framenr 43779250 at 45493736544442
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 3 in framenr 43779282 at 45493768453297 ***********
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 0 in framenr 43779314 at 45493800400955
Jan 12 22:35:09 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 174
Jan 12 22:35:09 vlieland kernel[0]: write wrap in usbframe 43779282 list 42 byte 366   ********** (quite far from frame 63 byte 564)
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 1 in framenr 43779346 at 45493832516894
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 2 in framenr 43779378 at 45493864390459
Jan 12 22:35:09 vlieland kernel[0]: input wrap in list 3 at frame 63 byte 564  ********** (framenr 43779282 above)
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 3 in framenr 43779410 at 45493896504445
Jan 12 22:35:09 vlieland kernel[0]: write wrap in usbframe 43779378 list 21 byte 180
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 0 in framenr 43779442 at 45493928515278
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 1 in framenr 43779474 at 45493960563209
Jan 12 22:35:09 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 378
Jan 12 22:35:09 vlieland kernel[0]: write wrap in usbframe 43779442 list 63 byte 564
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 2 in framenr 43779506 at 45493992474068
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 3 in framenr 43779538 at 45494024565908
Jan 12 22:35:09 vlieland kernel[0]: READ framelist 0 in framenr 43779570 at 45494056531349
Jan 12 22:35:09 vlieland kernel[0]: input wrap in list 1 at frame 21 byte 186
Jan 12 22:35:10 vlieland kernel[0]: write wrap in usbframe 43779538 list 42 byte 378
Jan 12 22:35:10 vlieland kernel[0]: READ framelist 1 in framenr 43779602 at 45494088463014
Jan 12 22:35:10 vlieland kernel[0]: READ framelist 2 in framenr 43779634 at 45494120512809
Jan 12 22:35:10 vlieland kernel[0]: input wrap in list 3 at frame 63 byte 570
Jan 12 22:35:10 vlieland kernel[0]: READ framelist 3 in framenr 43779666 at 45494152595618
Jan 12 22:35:10 vlieland kernel[0]: write wrap in usbframe 43779634 list 21 byte 186
Jan 12 22:35:10 vlieland kernel[0]: READ framelist 0 in framenr 43779698 at 45494184512247
Jan 12 22:35:10 vlieland kernel[0]: READ framelist 1 in framenr 43779730 at 45494216591087
Jan 12 22:35:10 vlieland kernel[0]: input wrap in list 2 at frame 42 byte 378

You can see that for example the marked wraps in read and write are way out of sync, 20 frames or so.

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

BTW strangely that the audio sounds perfect at 192kHz, in spite of this huge sync problem

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

Probably the timestamps used for the wrap are COMPLETION times. This means it's biased to the end of the wrap. This would cause a systematic error in the wrap time of 1/2 the usb frame length.

I tried to fix this bias by estimating the time that the byte came in from the distance to the end of the frame.

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

No, this approach is not working because we don't know (1) when exactly the USB did the transfer.
If we get timestamp 1ms the transfer may have happened anywhere between <0,1]. It's just that USB found at 1ms that the frame had been received.

But I can check the freedom USB has in the distribution of the packet. If I ask for bInterval 8, maybe USB is only allowed to use microframe 0, 8, 16, etc.

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

Checked WorkingWithUSB manual.

all isochronous transactions, including high-speed isochronous transactions, start on a frame boundary, not a microframe boundary

from emu-driver.

Wouter1 avatar Wouter1 commented on August 23, 2024

The async at 192k became apparent only when the eraseHead was enabled.
It's now fixed, see #30
The sync is at most 1 sample off in practice.

from emu-driver.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.