<< >>
justin = { main feed , music , code , askjf , pubkey };recent comments
search
Searching for 'ide' in (articles)... [back to index]

January 30, 2024
SOB 100k race report - I came to chew bubblegum and climb hills, and I was all out of bubblegum?

My last trail race was a bit over a year ago and apparently these experiences are some of the few I find worth documenting, so I'll bring the imaginary reader up to date on the the last year of running/hiking related activities/health/etc, even though my 2023-in-review post did some light quantification.

After Ray Miller I kept running and ran some road races in NY, including the Brooklyn Half, and a 5k a week later (when I wasn't fully recovered, which was also fun but probably too hard to soon). Later on in May I started having some leg nerve feelings, which ended up being caused by a herniated disc, so I had to cool it on the running for a bit. I started walking a lot. Pretty much any time I would normally bicycle somewhere, I'd walk instead. And as advised, I got a routine going of core strength exercises, and figured out how to continue my glute/hamstring PT. The gym, ugh. I think I read somewhere that gymnasiums were originally places where people would work out in the nude. Maybe the root was the greek word for nudity? anyway I digress. I find myself doing this in text messages too, saying way too much. Do I do it in person too and not notice it because there's no written record of it?

In the summer, Steve, Edward and I all signed up for the January 27, 2024 Sean O'Brien 50 miler. Edward and I ran this race in 2020, right before the pandemic, and joked about how covid would be nothing. When I signed up for the 2024 race, I wasn't running, did not know if I would be running by January, but I figured worst case I could try to power hike it.

I walked the NY Marathon in November (in 5:20 or so), which was a fantastic experience and I would recommend it to anybody who likes to walk. I took videos of a lot of the bands who played and then had a good Strava post which read as a tripadvisor review of the New York Music Festival -- too much walking! I should've posted those videos here. Maybe I still will. Let me know in the comments if you think that's a good idea.

A couple of weeks after the NY Marathon, I started running again, and worked up (with a covid-intermission) to a few 15-20 mile weeks, on top of 40-60 miles of walking. When I was running at my peak, the most miles per week I'd ever sustain was about 40, so I was feeling pretty good about the volume and time on my feet. Then, the week before the race, the SOB organizers sent out an email that mentioned you could change distances, and also that if you were running the 100k and you missed the 17 hour cutoff (or decided you wanted to bail), you could drop to the 50 miler during the race, at mile 43. So it became a fathomable thing -- sign up for the 100k, and if you're not feeling it at 43, just do 50. And not only that, if things go really poorly, it buys you another half hour for the 50 miler. Steve and I decided to do that.


(an old friend saw me off on my journey)

We drove to LA.


(this dog barked at me until I acknowledged him at the red light)


The (almost)-annual pilgrimage to Tacos Delta. Saw Paranorm. ChatGPT told us (and Wikipedia eventually confirmed) that Tarzana was named after the creation of its resident Edgar Rice Burroughs. Steve walked 15 miles the day before the race (!).


drop bags

Gear for the race:

  • I wore the most comfortable shoes I had, which were Hoka Clifton 9s, with probably 1500 miles on them (nearly falling apart, by the end they were definitely falling apart). I've been using the https://www.activeimprintsco.com Active Imprints insoles which are great for some mild form corrections.

  • At Edward's strong recommendation, I hiked with trekking poles. This turned out to be a huge help. Game changer. Kept me from tripping on my face countless times and the course has some steep climbs which you really can power up with. They were very common in use, and a few other runners I talked to regretted not bringing them (side note, what's the deal with the TSA requiring them to be in checked luggage? You're going to hijack a plane using trekking poles?! wtf).

  • Since the start was before dawn and the finish would almost certainly be after sunset, I needed a headlamp. I modded my Fenix HM50R to be waist-mounted (old SPIbelt, heh). And I had a cheapo 5 GBP LED headlamp as a backup. Seeing other people with waist-mounted lamps, though, makes me want one of those (much wider illumination area etc).

  • Backpack (Salomon Agile 6) with water bladder and stuff. I also carried my usual 24oz steel water bottle, which I usually keep and toss with my hands, which is enjoyable, but with trekking poles it was a pain in the ass, and while I could put it one of the front pockets of my backpack, it was a pain to take it in and out, especially as the day wore on. Noted.

  • I started trying tailwind a few months ago so I brought some packets of that, stroopwaffels, trader joe's peanut butter pretzels, and some almonds.

The race -- forecast was a low of 55 and a high of 72. Turns out the start was considerably colder, though, due to microclimates of the valley. But it was still quite pleasant having so recently been in the 20-degree highs of NY.


Psyched sideways

The first half of the race was more of a run than a race, as these things go.


The race begins on a road. Hey wait.

The water crossing at mile 2 was quite a bit higher and unavoidable this year. In 2020 I managed to keep my feet dry. The wool socks I was wearing quickly dried out and didn't give me any problems.









I changed my shirt and hat and dropped off my headlamp at the drop bag at mile 13, around that time I noticed some bits of chafing in spots, put some bodyglide on there and it stopped being a problem.

Peanut butter pretzels were good, stroopwaffels too. I think I might have accidentally dropped a wrapper though, ugh, sorry, hopefully someone picked it up. I put it in my pocket and closed the zipper but when I went to open the pocket at the aid station to dump the trash it was gone. Or maybe I misplaced it. Your brain sometimes loses track of all of these things.


why didn't someone tell me that water bottle looks ridiculous in the pocket?


group of mountain bikers having lunch, I assume. nice spot for it.


this time, on the descent to Bonsall, I could see the trail across the valley that we would later be on. makes me giddy!


settling in to the race and getting ready to fall apart at mile 22, Bonsall


At mile 22 I stopped, saw a guy (hi, Peter) whom I had previously mistaken for Steve, put some squirrel nut butter and a bandaid on a hotspot on my big toe (worked well, never used that stuff before). Filled up to the brim with water.



(crows getting a free ride on the ridge)

I paid more attention to birds this year, and not just the crows. I'd like to go back to these trails with a camera and big lens and time to spare.

The massive climb out of Bonsall was nice since I knew what to expect (in 2020 it was demoralizing, lol), but it was really hot. There was a stream crossing where dipping your hat in the water was an amazing feeling (though within minutes it was back to heat). If I had more time I would've sat in it.

The second half of the race was more difficult. I no longer had the energy to take pictures. The aid station around the halfway point had a lot of bacon. I really wanted some but I couldn't bring myself to eat any. This seems to happen to me at this point, nausea and stuff. I need to figure this out (brain thinks I have food poisoning or something?). Maybe I should've tried a gel. Doesn't require chewing and pure sugar, might have been worth the try. Hindsight.

At mile 37-ish, drop bag again, grabbed lights, long sleeved shirt, other hat. Didn't want to mess with my socks so kept my original pair.

I kept moving, snacking a little bit here and there, trying to down some tailwind along with the water, hanging on. By mile 43 (nearly 11 hours after the 5:30am start) I was 5 minutes ahead of my 2020 time, and only 10 minutes behind Steve, but I really couldn't eat anything. I overhead a couple of people drop to the 50 miler. My legs felt OK, and it turned out if I continued on with the 100k route, I could always drop at 50 miles (since it was a 6-mile each way out-and-back that turned around near the finish). So I continued on. Up a hill, then down a really massive hill. Going up the hill was fine. Going down the hill was difficult. I haven't done enough quad strength training. Tsk tsk. I ran a little bit of it but it was mostly walking. Ate maybe 3 almonds, drank a few swigs of tailwind. It was starting to get dark. At the bottom of the hill it was along a riverbed for a while. Lots of frog sounds. I saw Steve when I was about 15 minutes away from the 50 mile aid station (so his lead was up to about 30-45 minutes at that point, I guess?).

The aid station people gave me a quarter of a banana, which I ate. It was not easy. They were nice (they are all). Someone I talked to earlier in the race asked if I had a pacer for this part, then looked at me like I was crazy for not. I remembered this, and asked if there were any freelance pacers available. No such luck.

Did the riverbed commute back to the climb, now with my head(waist)lamp on. Coming down the hill was a race marshall, sweeping the course. Nobody else would be coming down the hill. I could see headlamps far ahead, and occasionally see them far behind me, but for a long time I saw nobody, and heard only the sounds of frogs and wind. The moon rose, it had a lot of clouds in front of it and looked very red on the horizon.

I running a huge calorie deficit and was having to regulate my climbing of the hill in order to prevent bonking. I'd go too hard and have to back off because I could feel it wouldn't be sustainable. This was the toughest part of the experience, I think, this climb. When I was eventually caught by another runner, it was nice.

Going over the hill and back down to the mile 43 aid station (again, though now at 55-ish), with 7 miles to go. This aid station is a bit remote and you can't drop out of the race there, and I guess it was getting late, so the aid station captain was really big on getting me moving. Tried to get me to eat, but when I did my best picky eater impression he said (very nicely -- everybody volunteering at the aid stations were amazing) something to the effect of "well water is what you need most right now, now get moving." So I did. I ended up not having any real calories to speak of for the last 20 miles of the race, heh. Though almost all of those 20 miles were walked, not run.

After that final aid station, the last 7 miles were challenging but also pretty straightforward, the finish was within reach, and I had plenty of time to not hit the cutoff at a walking pace. My underwear had bunched up and I had some pretty significant butt chafing but it was too late to do anything about it, just had to suffer with it. Should've checked in for it hours ago, doh. Once I got to the flat ground of the last mile, walking at about 13 minutes/mile to the finish felt pretty good (flat!). I was sort of hoping to be DFL, but not enough to slow down when I saw some headlamps behind me.


After more than 16 hours of running and hiking, Steve was waiting for me at the finish (having waiting 90 minutes! <3). There was food, but it would be hours until I could eat anything meaningful. We headed back to Tarzana, and watched some TV (was it Boys or 30 Rock? I can't remember) before crashing.

I got the shivers again. Seemed to be associated with movement, too. Didn't last too long, and not so bad. Way better than covid. Apparently it's about inflammation.


The next day Edward made us steak. Amazing.


There was ice cream, and a cold swim in a 55F pool. Total win.

Am I ready to do this race (including its 13,000ft of climbing and descent) again? No. But it won't be long.

4 Comments


January 16, 2024
old things

I posted some pictures in the previous posts here, and the lens I used is a 400mm f/5.6 Canon EF lens which I bought in 2001. I realized this was one of the older things that I still have and use. Yesterday as I was walking outside on a cold day, I realized that the mittens I was wearing, which I've worn a lot the last 10 winters, are probably similar in age. Other than a couple of guitars, I don't think I have much else that old. Everything wears out or becomes obsolete1. Boo.

[1] I also have my old Canon EOS-1 35mm camera. I haven't had the patience to use it in at least 15 years. And other lenses from the 2000-2001 era, which in recent memory I've only used for shooting video with the (newer) EOS-6D.

Comment...


January 6, 2024
Notes on software development these days, and C++

While I mostly program in C++, pretty much everything I write targets C++98 or C++03, and completely avoids the C++ standard template library.

I have no doubt that one can write exceptionally good and safe code using C++/STL, but it also appears to be incredibly easy to write very clean and safe C++/STL code that compiles without warnings or other notice into stupidly large, inefficient, memory-intensive machine code.

People new to C++ might write something that generates a list of strings:

  std::vector<std::string> list;

  // populate my list with strings, suppose this is in a loop
  std::string s = ...;
  list.push_back(s);
then later on decide to pass this list to a function:
  void dosomething(std::vector<std::string> list)
  {
    // does something
  }

...but nobody will tell them that each call of dosomething() will execute a std::string copy constructor for each item in the list, allocating (and subsequently freeing) memory for each string. There are just so many ways to get bitten here. Yes, I know, make list 'const &' etc, but it's very easy not to know that (or somewhat less easy but still possible to forget), and multiply this by 1,000 times (I know one person who was sufficiently burned by pre-increment vs post-increment having massive performance implications that they always use pre-increment in for loops, even if it's a basic type -- not that there's anything wrong with that, I guess post-increment is an odd thing to see as normal anyway)..

And we wonder why modern software generally sucks?

Recordings:

Yes, Exactly, Yes! - 1 - Sandwich Terrier (I) -- [4:47]
Yes, Exactly, Yes! - 2 - Sandwich Terrier (II) -- [3:45]
Yes, Exactly, Yes! - 3 - Sandwich Terrier (III) -- [3:38]
Yes, Exactly, Yes! - 4 - Virgins Again (Bad at Math) -- [3:27]
Yes, Exactly, Yes! - 5 - Prelude to the River -- [7:47]
Yes, Exactly, Yes! - 6 - The River -- [4:36]
Yes, Exactly, Yes! - 7 - Vertical Integration (I) -- [5:51]
Yes, Exactly, Yes! - 8 - Vertical Integration (II) -- [5:07]
Yes, Exactly, Yes! - 9 - May 29 Track 6 -- [6:34]
Yes, Exactly, Yes! - 10 - Las Vegas -- [3:57]
Yes, Exactly, Yes! - 11 - Terms of Service -- [5:32]
Yes, Exactly, Yes! - 12 - Hindenburg -- [8:19]
Yes, Exactly, Yes! - 13 - Chosen by the Few -- [5:20]
Yes, Exactly, Yes! - 14 - Self Imposed (I) -- [7:40]
Yes, Exactly, Yes! - 15 - Self Imposed (II, slower) -- [7:23]

5 Comments


November 10, 2023
arm64EC on Windows 11

Oh hi there, I guess it's been a while! REAPER v7 has launched. Right on.

I've spent some time on and off occasionally getting REAPER to run natively on Windows/arm64. There's practically no demand for such a thing, but the platform exists so it's worth thinking about. I read various reviews of some arm laptops that ran Windows and the real issue seemed to be lack of native software.

Anyway, in trying to finish these builds, I learned about something new in Windows 11/arm: the "arm64ec" ABI. This allows emulating x86_64 on native arm64 processes, with the ability for arm64 code to load/call x86_64 code without having to do any process bridging or anything like that. It's pretty much designed for DAWs, which need to load plug-ins in process for performance.

I posted a build of REAPER to our pre-release site which uses this. What cool is that there are a few libraries we ship with which we don't have ARM versions, but REAPER happily loads the x86_64 versions and they work! (one is libmp3lame.dll, haven't gotten around to making a native build of that.

The only real hiccups were porting the dynamic code generation of EEL2: symbols get a # prefix, there are a few registers you can't use, and you have to use VirtualAlloc2() to allocate code memory and tag it as being arm code rather than x86_64.

Not sure if this stuff will ever be worth releasing, but it's fun, and if you're interested you can play with it.

P.S. the new code signing requirement that the key needs to be stored in a hardware security module (e.g. Yubikey) is completely misguided. The Yubikey always wants you to enter the PIN, too. SMH. Additionally, if you remote desktop into a Windows 10 computer, you can't use the locally-installed HSM! So alas, I've installed TightVNC for the first time in more than a decade (not sure if that's a security win...).

3 Comments


October 7, 2023
C++ RAII optimization

Wanted to check my assumptions on C++ and the ability of modern compilers to optimize RAII (side note: I can never remember that abbreviation, always have to google it), so this morning I had a few moments of fun with godbolt:

    extern int gv, bar();
    
    template<class T> class saver {
      public:
      T save, *ptr;
      saver(T *p) : ptr(p) { save = *p; }
      ~saver() { *ptr = save; }
    };
    
    void foo_c() {
      int save = gv;
      gv = 1;
      bar();
      gv = save;
    }
    
    void foo_cxx() {
      saver<int> s(&gv);
      gv = 1;
      bar();
    }
    
    
When compiled with -O2 -fomit-frame-pointer -fno-exceptions, would foo_c() and foo_cxx() differ meaningfully? Output (x86-64 gcc 4.7.4, newer gcc versions are all similar):
    foo_c():
            push    rbx
            mov     ebx, DWORD PTR gv[rip]
            mov     DWORD PTR gv[rip], 1
            call    bar()
            mov     DWORD PTR gv[rip], ebx
            pop     rbx
            ret
    foo_cxx():
            push    rbx
            mov     ebx, DWORD PTR gv[rip]
            mov     DWORD PTR gv[rip], 1
            call    bar()
            mov     DWORD PTR gv[rip], ebx
            pop     rbx
            ret
    

So yeah, the optimizer does perfectly.

Recordings:

Yes, Exactly, Yes! - 1 -- [8:53]
Yes, Exactly, Yes! - 2 - Private Life -- [7:20]
Yes, Exactly, Yes! - 3 - Watch Your Step -- [2:46]
Yes, Exactly, Yes! - 4 - Watch Your Step (II) -- [2:05]
Yes, Exactly, Yes! - 5 - Self Imposed -- [4:39]
Yes, Exactly, Yes! - 6 - Dogs Will Rule the World -- [3:04]
Yes, Exactly, Yes! - 7 - Virgins Again -- [2:53]
Yes, Exactly, Yes! - 8 - Virgins Again (Again) -- [3:14]
Yes, Exactly, Yes! - 9 - The River -- [4:31]
Yes, Exactly, Yes! - 10 - Cosmic Background -- [3:32]
Yes, Exactly, Yes! - 11 -- [5:20]
Yes, Exactly, Yes! - 12 - Hindenburg -- [6:42]
Yes, Exactly, Yes! - 13 - Chosen by the Few -- [3:48]
Yes, Exactly, Yes! - 14 - Las Vegas -- [4:04]
Yes, Exactly, Yes! - 15 - No Big Benefactor -- [4:06]
Yes, Exactly, Yes! - 16 - Now We Understand -- [4:14]
Yes, Exactly, Yes! - 17 - Cast Iron Candy Bandit -- [3:10]
Yes, Exactly, Yes! - 18 - Dogs Will Rule the World (reprise) -- [3:37]
Yes, Exactly, Yes! - 19 - Find Me -- [5:01]
Yes, Exactly, Yes! - 20 - Old as Coal -- [4:33]
Yes, Exactly, Yes! - 21 -- [11:36]
Yes, Exactly, Yes! - 22 - Charlie (feat Cory Choy) -- [4:35]

Comment...


September 14, 2023
inefficient programming

Someone asked if I would post something about programming. I wish I had something interesting to offer. The best I can do is describe an area in our current work where there's a considerable productivity drain ("technical debt", I suppose).

We're still using Windows .rc files for dialog boxes. VC6 is my preferred dialog editor, but it has a limit of something like 32k or 64k controls in total for the entire .rc file, if you exceed that it crashes. We've long exceeded that, so now we pretty much edit .rc files by hand with lots of trial and error (I don't seem to be getting any better of adding/subtracting fixed values to the Nth column of a bunch of lines). We could temporarily trim the file, do the edits, then restore the rest, but meh. Or we could write a .rc file editor lol. At any rate it's completely inefficient.

On a more specific points related to that, adding options to REAPER's preferences takes far too much work. There's the manual .rc file editing, the juggling around of options in a fixed amount of space, moving things to other tabs, etc. It's stupid and a time suck. But there's no reasonable alternative without a ton of extra work. Some day, maybe.

So anyway, it's often the case where we want to add something simple, and a good half of the work is spent with UI nonsense. Yes yes we could move all of our preferences to a list of attributes and make it all generated from data and that would be great but that would be a huge project.

bonus:

People ask about making a mobile sequencer. The underlying core of a DAW would be the same, but doing the UI would require basically a separate implementation to be useable. I don't really want to maintain two DAWs. Also phones bleh (x 1000 -- the ecosystem, the lockeddownness, the lack of keyboard, the mercy of the OS, etc). :/

10 Comments


January 7, 2023
a (not) dangerous branch

Working on a fun REAPER branch relating to keyboard shortcuts. I will allow some pretty sweet things, but it will also give people a lot of rope to hang themselves with. You can have a bunch of alternate main keyboard mapping sections (which also affect mousewheel mappings), so you can switch sections by action/toolbar/menu/whatever.

You can also engage a section momentarily by action, so you could: make Ctrl+F be "momentarily use the main section named "FX", then that section could just have a ton of mappings to particular FX: Ctrl+F followed by E for ReaEQ, Ctrl+F followed by C for ReaComp, etc.

What's also fun is that when they are momentarily engaged, they act globally... so if you are in a text field and do Ctrl+F, then the following E or C still work, or if you have the Ctrl+F as a global hotkey, then that momentary switch makes the E or C effectively global too, but only when following the Ctrl+F.

But -- if you accidentally run an action perma-switching to a section that has no keyboard mappings... well nothing will work until you switch back via the correct action in the actions window, or you restart reaper. Which is where the rope to hang yourself is.

4 Comments


December 21, 2022
EEL2 Inception

(I posted this to Mastodon but really why not put it here too?)

In one of our REAPER development branches (and thus the latest +dev builds), there's now support for generating EEL2 code on the fly (for EEL reascripts, JSFX, video processors), using EEL2. I find this immensely pleasing.

To use this functionality, you can create an embedded script, using <? script code ?>, and that script has a separate variable/memory space, and can generate code using printf().

EEL2 often requires a lot of repetitive code to produce fast DSP code.

So for example, our loudness meter has a sinc filter that gets coefficients generated. Here's a before and after:

 function sinc_gen_slice(cs, o*) instance(sinc_gen_val slice_pos) global() (
   slice_pos = cs;
-  o.v00 = sinc_gen_val(); o.v01 = sinc_gen_val(); o.v02 = sinc_gen_val(); o.v03 = sinc_gen_val();
-  o.v04 = sinc_gen_val(); o.v05 = sinc_gen_val(); o.v06 = sinc_gen_val(); o.v07 = sinc_gen_val();
-  o.v08 = sinc_gen_val(); o.v09 = sinc_gen_val(); o.v10 = sinc_gen_val(); o.v11 = sinc_gen_val();
-  o.v12 = sinc_gen_val(); o.v13 = sinc_gen_val(); o.v14 = sinc_gen_val(); o.v15 = sinc_gen_val();
+  <? loop(x=0;sinc_sz, printf("o.v%02d = sinc_gen_val();%s",x,(x&3)==3?"\n": " "); x += 1) ?>
 );

There's some extra logic in there to produce identical output (including newlines) which isn't necessary, but added so I could test the output to make sure it didn't differ. The nice thing is, not only is this more readable and smaller, if you want to increase the filter size, you can without a ton of tedious and error-prone copy/paste.

Update, other tricks. If you want to make this code optional, you can do:

/* <? printf("%s/","*"); 
  printf("code which will only be executed on new jsfx/etc");
  printf("/%s","*");
?> */
and if you want either/or (new code vs legacy), you can do:
/* <? printf("%s/","*");
  _suppress=1; 
  printf("code which will only be executed on new jsfx");
  printf("/%s","*");
?> */
legacy code which will only be executed on old jsfx
/* <? _suppress=0; ?> */
(Forgot to mention earlier, you can set _suppress to have the preprocessor not output regular text)

2 Comments


December 4, 2022
Ray Miller 50 Miler Recap

(retroposted on December 17, 2022)

I took a ton of pictures on this race but I really have no use for them, nobody wants to come over and see my vacation slides, and I don't have a slide projector and also I took them on my phone which means they only exist as bits. I'm always looking for excuses to write about things, but it tends to be too much complaining about programming nonsense. So here are my photos, with explanations:




I flew into LAX on the Thursday. Decided to walk from the terminal to the rental car place. It's about a mile and a half, which if you've packed light is infinitely preferable to walk, especially in 55 degree weather, than to pile onto a rental car bus. Especially these days. I rented a car (brand new Kia sedan ftw), wandered my way around, got some lunch using a gift card I purchased in early 2020, mmm french toast, went for an easy run in Griffith Park to waste some time, it was really tough to not go more than a few miles. Checked into the guest house airbnb, went to bed at 7pm or so.




Friday involved going to Tacos Delta for chilaquiles. I'll leave this here. Kept to myself, worked a bit, went to bed around 7pm again.




Got up at 2:30am, left the airbnb at a little before 4am. Drove the long way to Malibu. Got to see places I occasionally went as a kid. Met Steve and Edward (and their friend Alex) at the start around 5:15am. There are other pictures with less fantastic lighting but really why when you have this one?




Something like 80 people, I guess? We start at 6am.

I should point out that the organizers mention that the course is well marked, but there is one turn that it's very important that the 50-mile runners make (there's also a 50k starting an hour later), and if you don't make the turn you'll end up running a bit over 50k. Why did I mention this?

The first few miles are a singletrack climb, which means the start position is very important. We held back, letting the faster people go ahead. Maybe we let too many. A little running, but a lot of fast hiking. Which was good, the last thing I wanted to do is go out too fast. I never know how these things will go, I mean usually it's fine but things will hurt and there will be suffering so better try to minimize it. And I'm on a low-mileage year. Anyway.




It starts getting light on the climb, 6:19am. Steve wants to go faster, runs a little bit of the climb, I somewhat keep up (catching up on the level bits or when he stops to take pictures).




It levels out a bit, there's some running, 6:40am. The aid station will come in another 20 minutes or so.




After the aid station (which was about 5 miles), some oranges, watermelon, water fillup, maybe some other snacks I forget now, we start a 6 mile loop with a little mud. Steve's in front of me here, at about 7:20am.




Ah the views, 7:28am.




The view ahead from the last shot, I love those shots of the trail ahead (or behind -- and more accurately the experience of seeing them in real life -- at home there's nothing more satisfying than seeing the Brooklyn Bridge ahead of you a mile away, and 1 WTC in the distance behind it, then finding yourself there by your feet). I'll try to point the trail shots out, if we can see with these low quality images I uploaded.




This one I could've left out, but hey 5 minutes passed and I can still see the ocean.









We head inland and it gets really muddy, I'm told this is due to rain. Tons of mud stick to your shoes, you end up tweaking your gait in order to try to shake it off as you go. Activating strange muscles. It gets a bit warm. Only a little bit. 7:49am. Another 10-15 minutes and you're back at the same aid station as before.




I left the aid station before Steve. As a result I was in front of him. There was a climb. I was not faster.




Oxnard, in the background, I assume! Our friend Fritz was always obsessed with that name. I meant to text him about that but didn't have much cell service. 8:26am.




After that climb, there was a pretty amazing descent, which I had a lot of fun on. Steve seemed to be on a phone call while he was running so I thought I'd give him his space. Near the bottom of the descent was the Valley of the Spiders[tm][cc](Andy). The spiders were considerate in marking their territory and I didn't feel threatened. 8:50am.




Just down the road a 100' from the spider web, it got really cold, this valley trapped that cold air, and the with the humidity there were these clouds forming. It was cool but didn't photograph well, just looks like dust here.




A short 10 minutes later, you pay the price of that descent, with a pretty steep fire road climb. I walked this (I walked all of the climbing really). As I was hiking up this, the leader for the 50k race ran past me. RAN. That did not look easy to do for a 10k let alone a 50k. At the top of that hill was the same aid station again (16 miles, 9:05am or so). Where I filled my water, ate some more, and left as soon as possible. Then about 100' down the road I stopped to clean out the mud from my sock which was messing with my big toe. Oh such stupid timing...




After that there was some nice downhill, then I ended up in a valley which was very warm and humid. This was probably as hot as I was all day, and it was at 9:30am.




On the climb out of the riverbed valley, it got cooler, but my hip was doing something funny, popping with each step that I walked (didn't have the issue running). I stopped to stretch a little, which didn't help, then found that I could adjust my gait slightly in order to avoid the popping. The popping didn't hurt but it also didn't seem like a good thing. The climb was nice, here's the view after crossing a gravel path that people were bicycling on (you can see them barely). It looked like a really nice place to ride. I do get jealous of California this time of year. 10:00am or so, 20 miles in.




I had a 10 minute stop at a NEW aid station, at 10:35-10:45 or so. At this point in addition to the eating and getting water (and watered down coke), I took my socks off, put some ointment and bandaids on my toes, which had been giving me problems (somewhat due to my mismanagement of them 4 weeks before at the NY marathon, another story which will not get documented). I also snugged my shoes slightly to try to keep some of the pressure off my toes (this ended up being an oops that I'm still paying for). Anyway 10-15 minutes after that aid station is this picture, mountains pretty. I like them. I start to climb them.




I go up what I thought was a really big hill (Strava tells me it was about 750'. OK not that big). At the top is this left turn. The one they mentioned at the start. It's a little tempting to just miss the turn and do a 50k. 11:15am, about 26 miles. I take the turn.

Side note: the course was very well marked, especially compared to the races in NJ I've done, but this is sort of the exception. They really should have a sign that says "50 mile runners: if you're not at mile 40 or so, turn around and make the turn!" Maybe missing turns and getting lost is a rite of passage for trail runners...




The climb continues! You can see the trail below... wait, shit, you can't. OK how about I "ENHANCE":




That's a person, I'm 79.3% sure. It could be Steve! I could easily check that in the days of Strava and GPS data but it's better not to know.




More climbing. 45 minutes and 2.5 miles later, nearing the top. This will be fun to go back down (we go out 7 miles or so and back another 7 miles to an aid station).




After that climb, there's still hills, some up, some down. I liked it better when the aid stations were closer together. You occasionally pass groups of tourists, which always feels odd, such differences in current experiences.




Still headed to the aid station, there's a view to the Northeast of some lake. I ponder whether it is artificial. 12:40pm, 31 miles, about to descend to the aid station.





Crow (or as I'm told, Raven), seen from the descent to the aid station.

Soon after, I arrive at the 33-ish mile aid station, and put on the best thing ever -- fresh socks. Long overdue. I try to eat some but it's getting more difficult. Sitting is nice. I arrive at around 12:50pm, leave around 1:05pm.

Out and back not so interesting, same thing in reverse. I see Edward before the big descent. Take a video of him running the other way. Share some ginger candy. He says Steve should be not too far behind him. We go our separate ways.





Take this video of running at the start of the big descent -- "Coming down the mountain" (around mile 38, 2:05pm)

I didn't mention it until now, but this whole race I've had songs from The Verve's "A Storm in Heaven" in my head. Butterfly, "The Sun, The Sun", Star Sail. I wondered if "The Sun, The Sea" is a reference to l'etranger. I'm not listening to anything but damn those songs just burn a hole in my ears. Maybe I get sick of them, or just my general starting-to-feel-awfulness projects to them (I still love that album and have happily listened to it since!).

I don't see Steve on the descent. I start to guess he missed the turn. D'oh.

The section of the climb that took 50 minutes to go up takes about 30 minutes to go down. Fun. My weak left calf starts feeling not great with every left step. Still fun though.




After that descent, some new territory. 2:30pm, around 39 miles. Eating ginger candies. So far between aid stations.




Nice trails though.




Ah another canyon, 3:15pm.




I think this was soon after the last one. The photographer was like "you're almost to the last aid station!" But those 3 or so miles took FOREVER. Boring flat jeep trail.

Get to the aid station, eat some stale Chipotle chips (they had water and gels and offered to make some soup but eh). Did some more toe management. This is the part of the race where I usually end up complaining and then after the fact regret the complaining. But anyway only like 4 miles to go, just one small climb and descent then it's done.




That climb went on way too long. The small climb is still 1000'. I talked to another person who was also complaining, though his excuse was that since September he had only run two 7 mile runs. pfft! and then he proceeded to go ahead and beat me by a solid 5-10 minutes. Ah, youth.




The climb kept going. More complaining.




Looking back.




My goal was to finish. Bonus to not finish in the dark. 4:30pm.




Damn, magic hour, 4:38pm. After taking this I enjoyed the descent, passed various hikers taking sunset pictures, we all agreed it was beautiful. Finished at something like 4:52pm. Took a few minutes before I could eat anything, had some quesadillas, chili, couldn't really stomach much. It was getting cold, so I took off. Saw in my text messages that Steve did in fact miss the turn (bummer).

Drove back to LA. Got back to the airbnb, ate some snacks, realized I didn't have enough of them, oops, took a shower, got chills, got in bed, felt really good under the covers. Whenever I'd get up to pee, massive shiver shiver chills.




7am Sunday, return to Tacos Delta. Glorious breakfast burrito made me whole. A few hours later, beer with salt and kebabs at Edward's. That was also amazing.

Thus concludes my story.

1 Comment


October 19, 2022
A Requiem for They Who Maintained Some Semblance of Equilibrium by Quietly Venting Into the Void


I wrote and recorded (though not always in that order) a couple dozen tracks over the last year, and decided it was time to put many of them into an album.

It's now available on my archive, Spotify, Apple Music, Amazon, Tidal, etc.

I look forward to continuing this journey, though I'm running out of old artwork to reuse for album covers.



3 Comments


May 28, 2022
ah yes



Comment...


May 11, 2022
Web3 (reprise) - WATCH

I've had nothing but contempt for Bitcoin and everything (afaik) that has spawned from that ecosystem, though apparently I've never written about it here. I have nothing but contempt for Bitcoin and everything that has spawned from that ecosystem! I'm hopeful that it's on its way out, but made sad by the fact that so many people have been scammed.

Having said that, it seems that the "Web3" movement wants to move the web past where we are now (which is where Facebook/Google/etc are almost everything), which is an admirable goal. If we can do it in a reasonable way. There's just no reason to build systems that try to be trustless or fully distributed, we already have a fantastic hierarchically distributed infrastructure for this: DNS.

In 2012, I had a thought along these lines -- and I wrote a Google Doc (irony much?) -- don't bother reading it though, it's very outdated and lacking in specifics.

To make the web more open and decentralized, what we need is a new protocol and piece of software!

WATCH - "We Are The Cloud Hosts"

The web that we live with today is largely because people want the following capabilities:
  • the ability to publish content to the public
  • the ability to publish content to trusted people
  • the ability to subscribe to content creators
Anybody can do all of these things without using Facebook/Google/blah, but it's not easy. Let's break it down:
  • the ability to publish content to the public

    Typically these require a web server to server the content. WATCH envisions a system where you could run software on multiple systems (your desktop, laptop when it's connected, cloud provider, etc), and it would automatically reproduce and sychronize your content. The protocol would allow connecting to a WATCH domain index in order to connect to the WATCH servers that contain data.

  • the ability to publish content to trusted people

    When making WATCH content requests, the requesting user could be identified by digital signature to ensure access rights.

  • the ability to subscribe to content creators

    AKA RSS. RSS feed generation and feed display would need to be integrated into this software.
This could all be accomplished by developing one peice of software: the WATCH server. In a typical scenario, the system would be setup as such:
  • An instance of the WATCH server running as e.g. watch-index.mydomain.com. If you have your own domain you could run it on a cloud VM, presumably email providers would be able to provide this service for your accounts if it ever caught on, etc. If a user wants data from user@mydomain.com, it would connect there, and (assuming it had permission), would receive a list of WATCH content servers for that account (which could possibly include watch-index.mydomain.com).
  • A few instances of WATCH servers running wherever. If you want your web presence to be very reliable you'd put them on VMs in a few AWS zones. If you don't care much you'd leave it running on your laptop or your desktop computer or whatever.
How it would be used:
  • Part of your WATCH account state would be a feed of content
  • To access WATCH feeds (yours, others, a mix of both, whatever), you'd point a browser at any of your own WATCH servers or indexes
  • For purely-public web pages, there could be a lot of opt-in shortcuts to this, having the index servers redirect links to content, things like that.
  • The WATCH server could provide browser-based and/or filesystem-based interfaces for posting content.
This is obviously not fully thought-through, but it does seem like some open software implementing this sort of infrastructure could be a really nice thing to have, and a good way forward for the web.

4 Comments


April 17, 2022
testing something



Comment...


March 18, 2022
e-bikes

I recently got my first e-bike, and I have some thoughts:

(I've been riding in NYC for a number of years, most days, somewhere in the thousands of miles per year. Most of the time I ride a Salsa Casseroll Single with fenders, rack, panniers, 20 tooth cog for easier climbs, and most recently a Surly Open Bar handlebars and a stem riser for upright posture. Side note: a video of a recent ride. )

The last few (six?) months I've been dealing with a hamstring issue (too much running with poor biomechanics, curse you teenage self), and bicycling does seem to aggrevate it, so I decided to get an e-bike with a throttle in order to be able to rest a little while still living life and going places by bike. I got a RadRunner 2 (it seems pretty decent, reasonably priced, and you can set it up to carry passengers. I have some qualms from setting it up but I'll save those for another day).

This morning I rode from Tribeca to Union Square to pick up a few things from the green market, and back, usingthe throttle almost exclusively. I found myself going a lot faster than I normally ride, and worryingly defaulting into a "driving" mentality. It left a bad taste in my mouth.

Later in the day, I rode to my office/studio in Red Hook, via the Manhattan Bridge, and made a special effort to go at a usual (leisurely) pace. It worked, and I managed to stay in a better, more peaceful frame of mind, but it took mental effort. I will have to continue that effort.

I find I definitely prefer the peacefulness of riding regular (*cough* acoustic *cough*), but my hamstring appreciated the electric-ness. What's interesting, though, is the economics:

I recharged my battery after riding 15 miles (which would be about 90 minutes of riding at city speeds), and using a Kill-A-Watt, I measured 360Wh of power use at the mains power. I looked up electric rates, and a ballpark we're talking about $0.05 worth of electricity, and the battery probably had less than 0.05% (or $0.25) of its lifetime wear. If I had ridden my regular bike, I would've burned a few hundred calories at least, and unless I was extremely frugal in my eating (I am not), there's no way I would spend only $0.30 on replenishing those calories. So in some ways, this is more economical (and probably more efficient, too?). Obviously there are benefits to exercise but let's assume I have that taken care of anyway.

Delivery people all moved to e-bikes ages ago. There were stories in the news about how they needed them in order to keep up, but I never really realized that the economics of it, even if you are as fit as possible, made generally cheaper to use electricity than to eat the extra food!

It's very good that e-bikes have been made legal in NY, hopefully the parks will follow (it feels like there should be an ADA claim against the parks that ban them, as there are people who can ride e-bikes but can't ride acoustic bikes). I'm still stunned by the efficiency of this bike, even with its massive 3" wide tires and weighing in around 65lbs.

(Update March 19: I was curious how much electricity electric cars use by comparison… sounds like typical is 34kWh per 100 miles, or 340Wh per mile… assuming that holds up in the city, that’d make the electric bike use about 1/15th the power. which is roughly in line with the mass ratio...)

Recordings:

nothing that will be missed

Comment...


February 6, 2022
interview with Paul Davis

Paul Davis (fellow human being and author of the Ardour DAW) suggested on Twitter that we should have a conversation which would be recorded and available for download/stream via the internet on-demand (apparently this is a thing now?).

It seemed like a reasonable (or dare I say interesting, given our common experiences) suggestion, and generally my willingness to talk to people has a sort of vaguely gaussian curve where the X axis they want to talk to me and the Y axis is how willing I am to talk to them: if they don't want to talk to me at all, or if they REALLY want to talk to me, then I'm pretty much not interested, but if they're in the middle, then sure why not?

We had our interview (which was enjoyable, and it even would have been without a pasty on my desk staring me down for when it was over) using NINJAM in voice chat mode, which mostly works amazingly well, until it doesn't (and then you have to reinitialize the channels -- I've been meaning to fix that for years but meh). I haven't (nor will I likely) listened to it in its entirety, but hopefully I didn't make too much of a fool of myself (thank you Paul for editing it). Paul also is being very modest about his interviewing skills, I thought he did an excellent job (for an emacs user), but we're always our biggest critics (except those instances where you get asshole critics, but to be fair they are usually just assholes and not so much critics).

Anyway, here's the interview.

One final note, after our conversation I do find myself wondering how good of musicians we would both be by now, had we not chosen to write DAWs and instead had spent the last 36 collective years with our time devoted to playing rather than programming. It's a completely unknowable and unrealistic question, anyway: whether or not you consider programming an art, it shares with art the most difficult part which is maintaining an interest in a particular thing for extended periods of time. Would I be an amazing guitarist if I'd spent the last 15 years playing guitar for 8 hours a day? Probably. Could I possibly spend 15 years playing guitar for 8 hours a day? No chance in hell.

4 Comments


December 31, 2021
Year in Review

Here are my notes:

  • Code: git commits from 2020's REAPER v6.19+dev1231 to v6.43+dev1231 for which I am the claimed author (excluding merges): 2,264
  • Code: cumulative changes (I'm not sure how to limit this to just my commits so it's global, including third-party SDKs and schwa): 1711 files changed, 132415 insertions(+), 121934 deletions(-)

  • Running (from Strava): 1,366 miles, 44,850ft of climbing, 219 hours. Mileage about the same as 2019, though far less climbing (2019 was 66kft) and a half dozen fewer hours. I've had something or other bothering me nonstop all year but only rarely keeping me from running (at the moment my left glute/hamstring is a bit tight). My high water year for miles was 2017, which was 1,444 (with similar elevation of 44kft, covered in 216 hours). Ah youth.

  • Bicycling: 3,148 miles, 92,560ft of climbing, 346 hours (I'm sure I forgot to record some rides but then there's also probably some GPS errors giving me bonus miles).

    These were mostly as transportation or leisure rides, I only rode the road bike 67 of those miles (sorry, buddy. I'll take you out soon). The brompton got about 200 miles (mostly in San Francisco), and the cargo bike even got 100 miles.

    The Casseroll single speed is my favorite (thanks, Alex!). This year I replaced its handlebars (Surly Open Bar + stem extender, and a cupholder), seat, front tire, bottom bracket, freewheel (after the old one failed in February, though it was fixed gear 3:1 for a good 6 months before going back to 12:5 freewheel), and twice (March and December) its chain. The brompton also got a new chain.

  • Music played with others: 62 sessions, producing about 87 hours of (very-loosely) edited music. The pandemic made this difficult. Singing with a mask on has its own challenges.
  • Music recorded alone: I did about 20 super8 sessions, producing about 9.5 hours of music/youtube videos. Also I recorded 37 doodles, about 2 hours of music.

  • It is probably for the best that I don't have many consumption numbers, such as paper products, disposable masks worn, etc.


Comment...


December 27, 2021
realtime audio on macOS in the age of asymmetrical multicore CPUs

It's now time when I bitch about, and document my experiences dealing with Apple's documentation/APIs/etc. For some reason I never feel the need to do this on other platforms, maybe it's that they tend to have better documentation or less fuss to deal with, I'm not sure why, but anyway if you search for "macOS" on this blog you'll find previous installments. Anyway, let's begin.

A bit over a year ago Apple started making computers with their own CPUs, the M1. These have 8 or more cores, but have a mix of slower and faster cores, the slower cores having lower power consumption (whether or not they are more efficient per unit of work done is unclear, it wouldn't surprise me if their efficiency was similar under full load, but anyway now I'm just guessing).

The implications for realtime audio of these asymmetric cores is pretty complex and can produce all sorts of weird behavior. The biggest issue seems to be when your code ends up running on the efficiency cores even though you need the results ASAP, causing underruns. Counterintuitively, it seems that under very light load, things work well, and under very heavy load, things work well, but for medium loads, there is failure. Also counterintuitively, the newer M1 Pro and M1 Max CPUs, with more performance cores (6-8) and fewer efficiency cores (2), seem to have a larger "medium load" range where things don't work well.

The short summary:

  • Ignore the thread QoS APIs, for realtime audio they're apparently not applicable (and do not address these issues). This was the biggest timesink for me -- I spend a ton of time going "why doesn't this QoS setting do anything?" Also Xcode has a CPU meter and for each thread it says "QoS unavailable"... so confusing.

  • If a normal thread yields via usleep() or pthread_cond_timedwait() for more than a few hundred microseconds, it'll likely end up running on an efficiency core when it resumes (and it takes an eternity in terms of audio blocks to get bumped back to a performance core, by which there's been an underrun and the thread probably will go back to sleep anyway). Reducing all sleeps/waits to at most a few hundred microseconds is a way to avoid that fate (though Apple recommends against spinning, likely for good reason). It's not ideal, but you can effectively pin normal threads to performance cores using this method.

  • Porting Your Audio Code to Apple Silicon was the most helpful guide (I wish I had seen the link at the bottom of one of the other less-helpful guides sooner! so much time wasted...), though it assumes some knowledge which doesn't seem to be linked in the document:

    You want to get your realtime threads in the same thread workgroup as the CoreAudio device's (via kAudioDevicePropertyIOThreadOSWorkgroup), and to do that you first have to make your threads realtime threads using thread_policy_set(THREAD_TIME_CONSTRAINT_POLICY) (side note: we probably should have been doing this already, doh), ideally with the similar parameters that the coreaudio thread uses, which seems to be a period and constraint of ((1000000000.0 * blocksize * mach_timebase_info().denom) / (mach_timebase_info().numer * srate), and a computation of half that. If you don't set this policy, it will fail adding your thread to the workgroup (EINVAL in that case means "thread is not realtime" and not "workgroup is canceled" per the docs). Once you do that, you do effectively get your threads locked to performance cores, and can start breathing again.
Perhaps this was all obvious and documented and I failed to read the right things, but anyway I'm just putting this here in case somebody like me would find it useful.

4 Comments


December 11, 2021
{frst rvlt}

{frst rvlt}: spoken word performed by Amir Parsa with Yes, Exactly, Yes! live at Hot Wood Arts on December 11, 2021. Video (with the exact same audio) is here on youtube.

Recordings:

Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 1 - Intro -- [3:24]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 2 - IN A DESERTED LANDSCAPE -- [3:27]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 3 - LA TIERRA LO SABE TODO (THE LEGEND) -- [3:40]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 4 - DIG -- [5:43]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 5 - FACES/NAMES -- [4:34]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 6 - LAST GAZES -- [5:30]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 7 - CON LUZ SIN LUZ -- [6:08]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 8 - CUTS BURNS CRIES -- [7:55]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 9 - FROZEN HOWL -- [4:09]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 10 - THE ISLE OF SCARS -- [2:29]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 11 - PÁGINAS BLANCAS -- [7:39]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 12 - RESTLESS BREATH DELIRIUM -- [4:53]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 13 - THE WAYS THEY FALL -- [7:43]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 14 - DON CRISTÓBAL COLÓN IS OFFENDED -- [6:56]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 15 - ELEGY FOR A DEAD REBEL -- [4:41]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 16 - THE FATE OF THE FOOT -- [10:44]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 17 - LOS ARBOLES TAMBIÉN ESTÁN LLORANDO -- [3:00]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 18 - NOTHERIGHTNAMES -- [4:22]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 19 - THE MUTILATED WORLD -- [2:28]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 20 - PAUSES AND PALABRAS -- [3:22]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 21 - THE BOOK OF SIGHS -- [5:45]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 22 - LA FIN D'ALL -- [1:54]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 23 - TO KI HASTI KÉ? -- [8:00]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 24 - THE ARCHIVES OF SILENCES -- [4:38]
Amir Parsa with Yes, Exactly, Yes! - {frst rvlt} - 25 - THE NEXT REVOLT -- [8:40]

Comment...


October 22, 2021
shut up new york



The sensation of paddling across the river on a warm October night,
lit by the lights of the city reflected back from the clouds, Jupiter
(or was it Saturn?) poking through, hypnotized by the smooth but
significant swells of the rising tide that (counterintuitively) tries
to send us to the harbor and out to the sea, the rhythms of the
neighboring boats paddling, rising and falling, things that probably
could be captured with technology but instead will be stored in wet
memory, dreamed about on the cold days to follow.



2 Comments


March 26, 2021
2006-me was an idiot

Hopefully in 2036 I'm not calling 2021-me an idiot, too. Here's an interesting old bug situation:

Dan Worrall posted a video on the 1175 JSFX, which was written by Scott Stillwell, way back in 2006, and graciously provided for inclusion with REAPER. In this video, Dan finds that the ratio control is completely broken, and posits a fix to it (adding a division by 2.1).

Sure enough, the code was broken. I went looking through the code1 to see why, and sure enough, there's a line which includes a multiply by 2.08136898, which seems completely arbitrary and incorrect! OK, so we see the bug 2. How did that constant get there?

When the 1175 JSFX was originally written, REAPER and JSFX were very young, and the JSFX log() (natural log, we'll call it by ln() from now on) and log10() implementations were broken. On a 8087 FPU, there are instructions to calculate logarithms, but they work in base 2, so to calculate ln(x) you use log2(x)/log2(e) 3. Prior to REAPER 1.29 it was mistakenly log2(x)*log2(e), due to the ordering of an fdiv being wrong4 and insufficient (or non-existent, rather) testing. So when that got fixed, it actually fixed various JSFX to behave correctly, and that should've been the end of it. This is where the stupid part comes in.

I opted to preserve the existing behavior of existing JSFX for this change, modifying code that used log() functions to multiply log2(e)*log2(e), which is .... 2.08136898. Doh. I should've tested these plug-ins before deciding to make the change. Or at least understood them.

Anyway, that's my story for the day. I'm rolling my eyes at my past self. As a footnote, schwa found that googling that constant finds some interesting places where that code has been reused, bug and all.

* 1 (beyond our SVN commits and all the way back to our VSS commits, all of which have thankfully been imported into git)
* 2 (and it will be fixed in REAPER 6.26, we're calling the non-fixed mode "Broken Capacitor" because that felt about right)
* 3 (or if you're clever and care about speed, log2(x)*ln(2) because ln(X) = logX(X)/logX(e) and logX(X)=1, but now my head hurts from talking about logarithms)
* 4 I haven't fully tested but I think it was working correctly on Jesusonic/linux, and when porting the EEL2 code to MSVC I didn't correctly convert it.


3 Comments


March 22, 2021
Ah, Big Sur

This most recent (as of this blog post) macOS update (and new architecture) has raised a number of issues with REAPER development -- I'm documenting them here in hopes of it being useful to someone:

  • If you're using the Hardened Runtime (for notarization) and have the com.apple.security.device.audio-input entitlement set, if you link against the latest SDKs you also _MUST_ have the NSMicrophoneUsageDescription set in your application's .plist, otherwise it will never prompt.

  • If you create a CGImage using CGImageCreate(), draw it using CGContextDrawImage(), and release it, that underlying image data is accessed by the system at a later time when the drawing actually occurs. This will cause all kinds of bugs in all kinds of software. We work around it by forcing Metal on various views where this isn't safe.

  • If you call MIDISend() with 250 or more events at the same timestamp, it will crash. (thanks to @helgoboss for isolating this)

  • Rosetta2 is amazing -- just don't try to get it to run x87 code. EEL2 still generates x87 code, so JSFX on REAPER-intel on an M1 is incredibly slow. A quick benchmark in EEL2 gave me this: arm64 native: 1.25s, arm64 bytecode: 4.18s, rosetta2 x86_64 bytecode 9.18s, rosetta2 x86_64 x87 code 59s. (bytecode is EEL2 in portable non-JIT mode). The benchmark I used was actually pretty low on math, I think on DSP code it's probably even worse, like a 100x slowdown.

    side note: so now, I'm trying to figure out the best way to deal with this. I did an EEL2-SSE branch a while back, and I have it working again, but it's about 20% slower than the x87 version in a lot of important cases. Tempted to make a jsfx-sse.dylib and only use it if running in Rosetta2. Also tempted to overhaul EEL2 to optimize for non-stack based architectures (which would also greatly improve the aarch64 version). The answer probably is something more incremental, like introduce some helpful optimizations into EEL2's code generation that will help get the SSE version up to parity. Of course, there will be little quirks that differ between the SSE and x87 versions, so those will need ages and ages of testing to get ironed out.


There were other things that came up that aren't as interesting. When launching Audio MIDI Setup.app, the new path is /System/Applications/Utilities/Audio MIDI Setup.app rather than /Applications, for example. Apple likes to change the appearance of NSTableViews and then provide ways to get them partially back to their previous style, but not all the way. Stuff like that... Dealing with macOS updates and compatibility is really not enjoyable. So tiring. Though the x87 vs SSE thing isn't their fault -- no x86_64 system has ever not had SSE...

4 Comments


January 2, 2021
oh a new year again

Well I failed at writing more during the pandemic. Shit.

I made a bunch of youtube videos w/ super8, including a tutorial which might be useful to some people:
(youtube link)

WARNING - DO NOT READ -- boring personal health/running related story follows. This is here for my own future reference, mainly:
In May of last year I was running a lot, laps on the roof and outside. I wasn't bicycling much. My knee started feeling weird and not really working right. Normally I'd go to the chiropractor, but with the pandemic I was avoiding things like that. So I tried massaging all of the bits around the knee, to try to loosen it all up. Bad move. I soon discovered that I had completely pissed off a bursa, which is a bad idea. That made it hurt to walk. By July I could run again, a little, but then I was getting a lot of pain around the bursa. More time off. Some bike rides, maybe not enough. In August I saw my doctor, and then saw an orthopedist. Got an MRI. MRI showed I had a torn meniscus (complex tear, medial horn, something like that). Ortho recommended surgery to clean it up. Finally went to the chiro, who was helpful. At some point I remember him applying pressure on the lateral side of the knee, pushing inward, and it popping a bit and then feeling better after it. Read a study about knee surgeries for this type of thing and how the differences of outcome were hard to measure between surgery and sham surgery. Read that MRIs can tell you about the tears, but there's no way to know whether they are a month old or 10 years old. I had a few things as a teenager that seem like must have done damage. Anyway, I was still having that bursa pain, but then found some crosswise friction massage on the tendon helped it almost immediately. After that I was able to run on and off in September to October. By November 6th I went to Forest Park and did 3 laps on the trails, which was great, but definitely used different muscles and motions vs road running. After that there was some soreness in the knee which felt deep, but after a day off I was able to start running again every day, with low miles at first, and November 7 until December 27 I ran every day with overall mileage increasing. Things didn't feel great all of the time but mostly they were OK. Anyway then on December 27, and maybe earlier but I ignored it, biomechanically things started going wrong. And after the Dec 27 run, the pain returned. A week has gone by since then, it's gradually been getting better, bike rides definitely help. (I just pushed from the lateral side on the knee and popped and that may have helped too, we'll see). So my current theory is that when I get tired enough and the right muscles weak enough, the knee's form changes such that it isn't operating correctly, which causes pain. Maybe I can adjust it back when needed, and strengthen the muscles to keep it working right? We'll see... So for now I've made myself a winter plan, which involves trying to bicycle ever day, not running more than 5 miles a day, not running more than 30 miles a week, and not doing speedwork. I'm putting that here so that I don't forget.
TL;DR: boring paragraph

Also those last Not Vampires NINJAM tracks were pretty fun! Favorites include Track 4 starting around 7:00 and Track 5 around 2:00

6 Comments


July 22, 2020
more super8ing

(youtube link)

Ah, I forgot how much I like using my super8 live setup. Some information on it:

  • Thinkpad X60 (old, old, old C2D 1.83GHz, 4GB RAM, recently upgraded to some incredibly cheap 100GB SSD). Recently I did find out that if you have a failed battery in the system, it kills the DPC latency. Having no battery is OK, though! Just don't pull the plug.

  • Zoom R24 (used as an audio interface and controller for levels of 8 channels of super8 output, buttons mapped to choose channels, toggle mic vs guitar DI input)

  • Line6 FBV MK II foot controller (mostly just use 3 buttons - one for record/play, one for toggling input distortion, one for toggling input delay).

  • Debian (Stretch I think?)

  • REAPER (obvs), with track 8 sending to a manual feedback loop that has a delay/compressor/eq/sometimes pitch shifter on it. "Save live output" is used to record the audio.

  • All built-in JSFX/reaplugs/etc, including super8 JSFX, spectral hold, some ReaVerb for cab modeling, etc

  • One (1) SM58. Used for drums, vocals, flute, sax, acoustic guitar, whatever else.

  • R24's hi-Z input is used for guitar/bass

  • Video recording: Contour Roam2 -- old, but not Thinkpad X60 old! I also use this to record the bicycle rides, so usually the brightness is a bit off (and the sensor has been failing in weird ways), so I mangle it heavily with a REAPER video processor to make it watchable.

  • One custom made wood stand (2x4s and stuff) to hold the laptop/interface
I really like the handheld camera treatment in this one, so I'll have to do more of that!

1 Comment


April 15, 2020
reapering at home

(youtube link)

This was a quick thing, using REAPER and the JSFX "sequencer megababy" and "super8", recorded and edited in 18 minutes while videotaping (taping hah). I'm going to do a longer version with more explanation of the tools used, I think. Lots of fun.

Recordings:

blanking on the date

2 Comments


April 1, 2020
another week

Well time keeps on ticking, I guess. I've done as NINJAM-related work in the last week as in the previous 10 years, I think. The jam we had last night (see below) was awesome, I especially like the last track in it.

We started renting a cheap ($20/month) server for hosting some private NINJAM servers. My initial thought was you could request a private NINJAM server via a web interface, then it would launch a new ninjam server process and give you the port number. That seemed like a bit of work, so instead I did something that would be less work.

As the first experiment I ran 50 NINJAM server processes on the one box, and had it as a purely etiquette-based system, where you only join an empty server or a server to which you were invited. It had some usership, though it was hard to tell if people really wanted them as private or if it was just a big farm of more servers.

I thought about modes where you connect to a server, and then can temporarily set a password so only your friends can join. Seemed easy enough, though you might connect to a server you think is empty and have someone beat you to it, and people might hog servers.

One thing I noticed is that the mechanism the NINJAM server uses to decide when to sleep was fine for a few processes, but if you ran 50 of them, all those polling between nanosleeps added up.

Then I thought -- what if you had a server where if you connected with a password, that password would define the room-name you go into (the NINJAM server already has a User_Group object which is basically the whole server). This seemed like a great idea, but it turns out it wouldn't work, because the password challenge/response system doesn't allow identification of passwords, nor would it detect if two users used the same password. I thought about using part of the anonymous username as a server ident, but the downside there is that if you set that, then connected to a different server with the same anonymous credentials, you'd leak your private server name.

So what I finally came up with: the NINJAM server can now run in a lobby-mode, and via chat commands you can create/join private rooms. You can also chat in the lobby with other people who are in the lobby. So you could negotiate "hey want to play an ambient session? let's !join ambient." So you have private rooms (secret roomname), semi-private rooms, etc. Feels pretty fun! Not many users yet, though.

Of course this will all get ruined once someone decides to troll/spam/flood/etc. I'm sure at the very start IRC was amazing and simple, and by the time I left it it was a mess with bots and anti-bots and other things that just ruin everything. Oh well let's enjoy it while it lasts.

(anything to distract from what's happening outside)

1 Comment


March 24, 2020
postpone 2020

Ah so it's been almost a week since I've posted here. Time got really really slow for a bit there but now maybe it is speeding up as life as we know it becomes more routine.

Such a bizarre predicament that nearly (?) all of humanity faces. We're all in it together, unless we're in denial. When the weather has been nice (most days since I last wrote), the parks have been crazily busy, which makes running really difficult. So instead:

(lucky me with roof deck access) An hour is an excruciatingly long time. At one point before I tried an hour I contemplated doing 24 hours. Now I think that's insane. It might happen. Yesterday was cold and rainy and amazing, I could run wherever I wanted and nobody was around! Bring back winter!

I'm now on board with everybody wearing masks (not N95 but procedure masks etc) when they are out in public. It makes a lot of sense, given the fact that people are contagious before and/or without symptoms. The advice here has always been "wear a mask only if you're sick" which is no longer applicable! Anyway I've spent a great deal of time making a mask (and now starting a second) from this site's template/instruction, which seems to work well! I don't have a sewing machine so I'm using a couple of tiny travel sewing kits that I've acquired over the years and doing all of the stitches by hand. I'm terrible at it, but getting less so as time goes on.

Musically, I've turned to NINJAM at home again, Andy and I played the other night which was nice. Hope to do more of that!

(obviously it goes without saying, all the people out there who are helping other people: doctors, nurses, delivery people, people working in grocery stores, thank you thank you thank you. I'll try to say this more in person but it's hard with all of the other pressures of life outside of home)

9 Comments


March 18, 2020
Super8 Panic Marathon

(youtube link)

Apologies for my outfit (unless you're into legs, in which case, you're welcome), I ran to the studio from home. At 11am the Brooklyn Bridge was pretty empty, thankfully, but on the return it was getting more crowded. Plenty of tourists out walking in big groups, not practicing social distancing in any form. Sigh. Can't stop what's coming.

There were also a lot of people out in Brooklyn Bridge Park. I don't think playing volleyball today is a good idea.

Maybe I'm overreacting, but it's really not looking that way. I hope I'm overreacting. A "shelter in place" order seems not-unlikely for NYC at some point in the near future. We've discussed leaving, but have nowhere we'd want to go. Being home is a pretty good place to be, even if there are too many goddamn people around. Even if we had known what was coming months ago, I'm not sure where we'd want to be. I want to be somewhere else but nowhere else. Maybe after this whole crisis is over (will it be a few months? 18 months? ugh), a move is in order.

Recordings:

super8_panic_marathon - 1 -- [0:17]
super8_panic_marathon - 2 -- [50:37]

4 Comments


March 15, 2020
Super8 (Social Distancing)

I bicycled from Manhattan to Brooklyn to play the piano some, and do a little Super8 session:
(youtube link)

On Wednesday, I had gone to the studio (which is right by Fairway) in the afternoon and stayed until the evening. I went into Fairway, which was pretty normal-ish. After my cancelled band practice and trip home, there had been a presidential address, which was a total failure in pretty much every way I could see, other than getting peoples attention. The next day, I went to the studio, and stopped in Fairway on my way home to get a few more things, and it was a total madhouse. Such a difference in a day.

Anyway since Wednesday we've been practicing Social Distancing, which is both easy and not easy. It's only been 5 days but it's going to be challenging.

A friend and I discussed food strategies via text. What Allison and I are doing now is basically trying to keep a stocked pantry with a list of things that we wouldn't mind having more of, and when we need something, we'll go buy that thing we need and other things from the list of things that we wouldn't mind having more of.

Last Sunday we were scheduled to fly to California, and yesterday I was scheduled to run a 50 miler in Marin County. We cancelled our trip the day before, which made me sad but after an exercise in sunk cost (figuring the difference in cost to proceed with the trip vs cancel, ignoring non-refundable stuff), it made total sense. Yesterday I found out the race was cancelled anyway.

There's my rambling. Someone on twitter said now was a good time to try journalling. My first thought is "my filesystems are all ext4, baby." I lied, I'm on a mac at this moment.

Recordings:

super8_distance - 1 -- [32:10]
super8_distance - 2 -- [5:56]
super8_distance - 3 -- [0:49]

Comment...


March 11, 2020
Super8 Cancel Everything

(retroactively posted on March 15)

(youtube link)

This was last Wednesday -- I was scheduled on this day to play music with some friends, but we cancelled after one of my friends found out that an usher at a Broadway show that he had recently seen had tested positive for Coronavirus, and the reality of this whole situation became clear. So I did a quick two songs before going home. The title refers to the article Cancel Everything in The Atlantic.

Recordings:

live solo improv: super8 cancel everything

Comment...


March 3, 2020
Windows with more than 64 cores

Windows has a unique (as far as I can tell) "feature", which I have not experienced first hand but I've heard about. If your system has more than 64 cores (effectively, e.g. 40 cores each with 2 threads would count), then it divides the cores into "processor groups." By default, an application that creates threads and does no additional work has all of its threads run in the same processor group (is this always Group 0? or is it randomly assigned? I have no idea).

The significance of this is that if, for example, you have a 36 core hyperthreaded CPU (72 logical cores), then a traditionally-written multithreaded application can utilize at most 36 cores. (It's also unclear to me if the 36 cores in a processor group in this scenario are 18x2 threads each, or 36 threads on 36 unique cores).

Anyway, we're now testing a REAPER tweak to address this. If you need to implement this in your application, see SetThreadGroupAffinity(), and get ready for some fun using GetLogicalProcessorInformationEx() with RelationGroup and parsing the output to determine the group structure (the documentation for that is a bit light IMO!).

So there you go, everybody.

Recordings:

super8 - 1 -- [17:37]
super8 - 2 -- [11:51]
super8 - 3 -- [2:38]
super8 - 4 -- [14:28]

2 Comments


February 11, 2020
SOB 50

Oops I haven't updated here in months. I posted a thread to Twitter the other night, which I'm now going to post here since it's more appropriate here anyway (I was tired and lazy and on my phone). So here goes (with [edits]):

Yesterday [Saturday, 3 days ago] I did my first 50 mile ultra[marathon]. It was in California, near Malibu [Edit: The race is called the Sean O'Brien 50]. I woke up at 3:30am, left the house at 4:10, picked up @runviper [Edward] at 4:35, arrived at the start at about 5:15, before sunrise. It was cold, 40F [reminded me of home], and since the full supermoon was low on the horizon, dark.

You will see a lot of me making this face in this thread:

The start was supposed to be at 6:00 sharp, though I'm pretty sure it was 30 seconds late:

[That's the organizer with the megaphone, I believe. In the final race information email, there was a really thoughtful paragraph which stuck with me, I'll quote it here:

    I believe we are capable of anything we set our minds to. If you visualize it enough, and work hard you can make it happen. Remember that it's a "gift" that we get to run on the trails. There are people who can't even get out of bed. You "get" to participate in an ultra. Enjoy the experience. Be in the moment, and just have fun. It will all come together on race day if you stay positive, and remember what a blessing it is to do what we do. Not even 1% of the population will ever do what you are going to do in less than 1 week. Pretty awesome when you think of it like that? See ya soon my friends!!
Anyway...]

The first couple miles were crowded enough that I didn’t stop to take a picture. It went up a nice hill which made me feel powerful in my ability to climb it while running, then down the other side, through some more park, and then we arrived at a stream.

It was perhaps a small creek, but big enough that crossing it was tricky. There was a loose rope going across to help. A headlamp went flying and started floating down the water. I had a moment of heroism when I recovered it. I crossed with dry feet. Not bragging. After the creek crossing we started climbing again, and the sky started getting light.


and then the sun rose. Around this time I was able to feel my fingers again. I had thin cherry tree 10-miler branded gloves on but they could only do so much. This was almost an hour in, probably around 4 miles in, having climbed about 1500’

After we ascended another 10 minutes or so, the fog covering the Pacific came into view:




There was a photo-op moment going up over some rocks. Approaching it I figured that round thing would be a microwave dish or something but it was actually a light for a photographer.. I’ll see the results eventually I imagine. [edit: that photo wasn't great and too expensive to buy!]

[The] first aid station was about 7 miles in (hour and 40 minutes after the start). Mmm watermelon and oranges. Also potatoes and salt. Eating felt good. [Spent about 2.5 minutes here]

The next hour or so was mostly single track and had a good amount of variety. The charcoaled wood from the fires of last year offered a contrasting element to the blissful joy of the run.





At about 9am (3 hours elapsed, about 13mi, 3200’ ascent) after crossing above a tunnel, we arrived to the second aid station, which had expanded offerings from the first. Sandwiches! PB&J awesome! In hindsight I should’ve had some candy. Regretting not having candy. Getting warm.


There were drop bags at that aid station... dumped my wool shirt, headlamp, picked up sunblock. [spent about 7 minutes here] Soon enough it got very bright, and less photogenic. (note re: video — Spaceballs reference, had plenty of water):

After another hour or so (10:15am?) we crossed over a pass and could see the marine layer again. ~17 miles and 4300’ climbing cumulative...



10 minutes downhill and we arrived at an aid station. Lemonade, fruit, sandwiches, potatoes, consumed. @runviper made a taco, I questioned his judgment for eating beans, then proceeded to join him. No regrets [on the beans] (for me at least) [regrets on not eating candy]. [spent about 5 minutes here]

At this aid station they explained we were 19 miles in, we just had to do 3 miles down to the next aid station, then 8 more miles back up another trail, then the 19 miles back to the start. Legs felt pretty good... time to descend. Oof.


3 miles, 1500’ of descent, and maybe 30 minutes later, the cracks started to show. That tight IT band thing you feel sometimes? hello


eat eat eat [spent about 7 minutes] then back up the hill with full water, going back to where we were, in 8 miles instead of 3. Hey why is it so steep?



After having climbed 1800’ for an hour, you suddenly realize you’re on top of the wrong mountain [edit: but still on the course -- it is a torturous course], and the aid station is a tiny speck on the horizon. There is a gorge separating you from it.

The aforementioned IT/knee thing made the 800’ descent difficult, especially the steeper parts. So I was actually happy to be climbing again, which was good because there was 1000’ to go for the aid station


These 8 miles were brutal. The sun was strong, it was hot, and seeing the expanse between you and where you need to be was intimidating. And knowing once you get to the aid station, you still have 19 miles to go (which are largely downhill, ugh) After having gotten to the aid station, food [nutella (gnutella) sandwiches, mmm. apparently they had run out of water too, but had since gotten more], ice in the face, etc [spending about 8 minutes], we continue on. There’s a small descent, a few hundred feet later I decide I must stop and get a rock out of my shoe. We are 31 miles in, 7300’ of ascent, it’s 1:40pm, there have been rocks in my shoes all day. I probably also tried to stretch my IT. anyway we climb 600’ to go back over the pass, and look back at the ocean:


Now it’s just a short 6 miles to the bag-drop aid station. At some point around here I started using anti-chafe stuff everywhere i felt twinges. Seemed to work but could’ve been placebo. I was wincing on all of the steeper descent bits, not taking too many photos

Get to the mile 37 aid station, change shirts back to wool, grab headlamp. Eat a little but damn at this point I’m sick of food. [Should've started eating candy. changed socks, win. Drank cold brew coffee from drop bag. Both win. Also did some stretching of the IT. total time here was 13 minutes]



And another 6 miles of mid-afternoon. With 2000’ of ascent (not too gradual, plenty of my new favorite thing at this point: descent)




We get to the final aid stop at around 5pm (that 6 miles took a while!), just 7 miles to go! Stretch again here [spent about 8 minutes here -- total aid station time was about 50 minutes]


After this aid station it really got to be the magic hour:



and the fog and mist:

the full super moon returned, too




the anticlimactic ending is that I stopped taking pictures, we turned on headlamps, I endured the descents, and in the last 2 miles got my feet soaked trying to cross the stream that I had managed to cross dryly in the morning [tweaked an old injury in my arm doing this, though, hanging on to the rope crossing the stream. didn't really realize it at the time, but it became apparent by Monday], and despaired that the trail never seemed to end.

and then finally finished 50ish miles with approx 11,000ft of ascent and 11,000ft of descent, in a bit less than 13 hours. And @runviper [Edward] was kind enough to wait for me to catch up before crossing the finish.

update: Monday: legs feeling pretty good! had some nice walks yesterday and a hike today. much better than post-marathon, which makes sense since most of it was hiking...

update: Tuesday: flew home, had an easy run.



Comment...


October 20, 2019
Decanted Youth, live Open Studios video

We played again in the studio last weekend, here's the first of two shows:
(youtube link)
(the next show will be posted soonish)

Comment...


July 8, 2019
macOS screen updating, part III: 2019

Five years ago, in the year of our lord 2014, I wrote about the difficulties of drawing bitmapped graphics to screen in macOS, and I revisited that issue again in the winter of 2017.

Now, I bring you what is hopefully the final installment (posted here for usefulness to the internet-at-large).

To recap: drawing bitmapped graphics to screen was relatively fast in macOS 10.6 using the obvious APIs (CoreGraphics/Quartz/etc), and when drawing to non-retina, normal displays in newer macOS versions. Drawing bitmapped graphics to (the now dominating) Retina displays, however, got slow. In the case of a Retina Macbook Pro, it was a little slow. The 5k iMacs display updates are excruciatingly slow when using the classic APIs (due to high pixel count, and expensive conversion to 30-bit colors which these displays support).

The first thing I looked at was the wantsLayer attribute of NSView:

  • If you use "mynsview.wantsLayer = YES", things get better. On normal displays, slightly, on a Retina Macbook Pro's display, quite a bit better. On a 5k iMac's display, maybe slightly better. Not much.
  • Using the wantsLayer attribute seems to be supported on 10.6-current.
  • For views that use layers, you can no longer [NSView lockFocus] the view and draw into it out of a paint cycle (which makes sense), which prevents us from implementing GetDC()/ReleaseDC() emulation.

After seeing that enabling layers wasn't going to help the 5k iMacs (the ones that needed help the most!), I looked into Metal, which is supported on 10.11+ (provided you have sufficient GPU, which it turns out not all macs that 10.11 supports do). After a few hours of cutting and pasting example code in different combinations and making a huge mess, I did manage to get it to work. I made a very hackish build of the LICE test app, and had some people (who actually have 5k iMacs) test the performance, to see if would improve things.

It did (substantially), so it was followed by a longer process of polishing the mess of a turd into something usable, which is not interesting, though I should note:
  • If you want to update the entire view every time, you can configure a CAMetalLayer properly and just shove your bits into the CAMetalLayer's texture and tell it to present and avoid having to create another texture and a render pipeline and all of that nonsense.
  • If you want to do partial window updates (partial-invalidates, or a GetDC()-like draw), then you have to create a whole render pipeline, a texture, compile shaders, blah blah blah ugh.
  • There's a bunch more work that needs to get done to make it all work (and adapt to changing GPUs, blah blah blah)...
This stuff is now in the "metal" branch of swell in WDL, and will go to master when it makes sense. This is what is in the latest +dev REAPER builds, and it will end up in 6.0. I'm waiting for it to completely bite me in the ass (little things do keep coming up, hopefully they will be minor).

As one final note, I'd just like to admonish Apple for not doing a reasonable implementation of all of this inside CoreGraphics. The fact that you can't update the 5k iMac's screen via traditional APIs at an even-halfway-decent rate is really stupid.

P.S. It does seem if you want to have your application support Dark Mode, you can't use [NSView lockFocus] anymore either, so if you wish to draw-out-of-context, you'll have to use Metal...

Recordings:

Decanted Youth - 1 - Supposed to Be -- [8:14]
Decanted Youth - 2 - (Vaguely Instrumental) Legacy -- [16:11]
Decanted Youth - 3 - (Vaguely) Round and Round -- [5:15]
Decanted Youth - 4 - The Squeeze -- [6:31]
Decanted Youth - 5 -- [3:16]
Decanted Youth - 6 - (mini cover medley) -- [10:03]
Decanted Youth - 7 -- [9:15]
Decanted Youth - 8 -- [8:26]
Decanted Youth - 9 - Trees and Mold -- [9:37]
Decanted Youth - 10 -- [4:53]
Decanted Youth - 11 -- [9:05]
Decanted Youth - 12 -- [7:02]

6 Comments


May 21, 2019
Copenhagen Marathon

This was the second marathon road course I've run (after NY) -- while the course was nearly flat, it was challenging because of:

  • Lots of people going through many narrow sections,
  • Curbs you could step off of wrong if you were not watching your feet (I partially rolled my left ankle in the first mile, rude awakening. Felt it for the next 10 or so)
  • The warm weather (mid 60s and sunny, the sections in the shade were pretty pleasant but in the sun you cooked!)
I wasn't prepared for this race in general (signed up for it a week or so ago), and wasn't optimistic about even finishing (the last month had seen no runs over about 5 miles). I positioned myself between the 3:40 and 3:50 pace group balloons, figuring I'd start by running 9s.

For the first half (and after my ankle rollover especially) I just hung out with the majority of the runners I was near, running 9s as planned. I didn't see too much passing, but maybe it was subtle? The first half went in just about 2 hours, and a bit after I stopped to stretch a little, massage my right calf (which had been getting tight on longer runs causing tendonitis in my ankle, which is why I hadn't done any long runs in ages), and my left ankle seemed to be fine, so I kept on. Around mile 20 I realized that a) I was going to finish, and b) I was actually feeling pretty good considering (my HR had been 130-140 or so the whole time), so I decided I'd run some 8s when the crowded course allowed and get some of those newfangled negative splits, which was successful.

Other notes:

  • Seeing Al and friends on the course was awesome.
  • Swimming in the harbor after was incredibly cold and perfect and 10/10 would do again.
  • Aid stations have signs ahead of them that say "OPEN BAR 100 meters ahead" which I find hilarious.
  • Aid stations use a ton of plastic cups.
  • I carried a backpack with me which turned out to be handy (extra water, ended up having a liter or so -- the aid stations were few and far between and as a result crowded and difficult.. but even if I stopped and had 2 waters + 1 energy drink at each, I would've still finished very dehydrated).
  • If you (like me) are not too familiar with Copenhagen, you don't really notice the loop in the course, because by the time the second time comes around you're in full "make this shit happen" mode.
  • The dry-bag backpack that they gave everybody is a fantastic perk, especially given how affordable this race is (compared to NY at least!).
  • Copenhagen is really lovely. <3

Video from my sunglasses: (youtube link)

1 Comment


December 12, 2018
(Hobbyist) Music Delivery

I've been recording 1-3 hour (occasionally longer) sessions of music and posting them to the internet as bigass .mp3 files for about 15 years now. As the quality of the music has steadily icnreased, I've also been looking at ways of making it more accessible (very few people can commit to listening to 2 hours of a single mp3). So after the last session, I thought it would be nice to mix it into individual tracks, naming them as I edit the session.

What I determined was that individual tracks were great! I thought about it some more and decided I could have both, generating the bigass mp3 files and indexing them (much as brainal.org does, but more intelligently. And then I realized I could parse the .RPP (REAPER project file) projects from the last 12 years or so, and generate a list of songs (from places where it was clear everything was edited) for each session. It doesn't work totally reliably, as there are plenty of places where two or three songs flow into eachother. But that's OK.

Anyway, so you might have noticed on this page the full jam links have been replaced with individual song/supersong links. We've been going through naming them as appropriate.

The other nice thing about this is that we can pull these feeds into our band websites...

13 Comments


October 29, 2018
REAPER: AVS's Spiritutal Successor - or - Decanted Youth - Light Creeps In (WIP)

I've been working on REAPER's video rendering/processing pipeline, allowing video processors to access and share memory with JSFX and ReaScripts, and exposing some new APIs for things like FFTs. It's pretty low-level, the JSFX/video processors are responsible for a lot of stupid work like synchronizing with eachother... but: the key thing is that we (REAPER users) can now effectively build things like AVS into the rendering pipeline, combining video and rendered audio and rendering that to video. I find this really awesome and exciting in the way AVS was, and in a way that has been missing for a long time. And now we can render AVS-like stuff directly to YouTube videos.

To explain: why is this like AVS? AVS had two key things that made it really cool:

  • You could build complex things by putting simple things together
  • You could also write complex things in code (making those "simple things" a little less simple).
  • (I'm bad at counting) (ok REAPER doesn't really address this -- AVS was cool because awesome people made tons of awesome presets for it. but REAPER's made for creators, so I guess we don't have to fulfill this one, so two things.)
REAPER does this too -- you can use video material directly, or one of many video processor presets to do basic things. Then you can also do the second thing by coding video processors (or hacking existing ones) to do new things. As an explanation, the effect in the video below is a combination of an existing preset (the spectrum analyzer), and a little bit of code (15 lines or so) which transforms that into polar coordinates (I could save that second bit of code as a preset for re-use later, including on a video file, etc).

The future is awesome. Now if only the present could catch up.

(youtube link)

Addendum: I'm irrationally pleased by this bit of video processor code I spent far too long on, for converting HSV (360/1/1) to RGB:
function h6s2i(h) global() ((h -= floor(h*1/6)*6)<3 ? <1 ? 1-h : 0 : h<4 ? h-3 : 1);
function set_hsv(h,s,v) global() (
  h*=1/60; s *= v;
  gfx_set(v-h6s2i(h+2)*s,v-h6s2i(h)*s,v-h6s2i(h-2)*s);
);
Also here's another video from testing that I meant to post earlier:
(youtube link)



2 Comments


October 15, 2018
retrospective ride

(youtube link)

2 Comments


October 11, 2018
Super8

(youtube link)

Side note: apparently the only thing I ever post here is YouTube videos. Weird.



Recordings:

andre - 1 -- [9:35]
andre - 2 -- [12:01]
andre - 3 -- [18:01]
andre - 4 -- [4:10]
andre - 5 -- [7:56]
super8 - 1 -- [17:09]
super8 - 2 -- [9:56]

1 Comment


September 26, 2018
super8 with andre's cocktail kit

(youtube link)

(posted a day late -- also, stupid vertical video, heh)

Recordings:

super8_cocktail - 1 -- [18:37]
super8_cocktail - 2 -- [0:39]

Comment...


September 25, 2018
video of untitled project (not vampires, maybe?) with andre and andy

(youtube link)

(posted after the fact)

Recordings:

Yes, Exactly, Yes! - 1 - Angry Teleprompter -- [23:52]
Yes, Exactly, Yes! - 2 - Rendered Circumstances -- [8:59]
Yes, Exactly, Yes! - 3 - Not Like Them -- [8:08]
Yes, Exactly, Yes! - 4 - Lugubrious -- [10:34]
Yes, Exactly, Yes! - 5 - Reflecting Scars -- [3:47]
Yes, Exactly, Yes! - 6 - Reflecting Scars (reprise) -- [8:02]
Yes, Exactly, Yes! - 7 - Metallic Dinosaurs Without Remorse -- [18:35]
Yes, Exactly, Yes! - 8 - Candy Bandit -- [9:47]

Comment...


September 19, 2018
video of untitled project with andy and andre

(youtube link)

(posted after the fact)



Recordings:

saywhat
Yes, Exactly, Yes! - 1 - Unexpected Gyrations on the Battlefield -- [15:00]
Yes, Exactly, Yes! - 2 - Binary Stalkers with Sensitive Afflictions -- [44:58]
Yes, Exactly, Yes! - 3 - Serious Rendezvous -- [10:47]
Yes, Exactly, Yes! - 4 - Emotionally Bewildering -- [12:22]
Yes, Exactly, Yes! - 5 - Saucy Drone -- [7:42]

Comment...


September 12, 2018
video of untitled project with andre, andy, sarai

(youtube link)

(posted after the fact)



Recordings:

Decanted Youth - 1 - Words Without Friends -- [7:33]
Decanted Youth - 2 - Don't Panic -- [6:26]
Decanted Youth - 3 - What If We All Just Disappeared -- [8:32]
Decanted Youth - 4 - Legacy -- [8:53]
Decanted Youth - 5 - Oath, Please? -- [5:13]
Decanted Youth - 6 - Spring -- [6:17]
Yes, Exactly, Yes! - 1 - Impulsive Whispers -- [15:42]
Yes, Exactly, Yes! - 2 - Small Talk -- [8:11]
Yes, Exactly, Yes! - 3 - The River Prelude -- [9:17]
Yes, Exactly, Yes! - 4 - The River (Early) -- [12:14]
Yes, Exactly, Yes! - 5 - Pistol-Whipped Apparitions -- [10:16]
Yes, Exactly, Yes! - 6 - Toxic Crisis / Toxic Crisis Averted -- [15:27]
Yes, Exactly, Yes! - 7 - Pillows on a Beach -- [9:13]

Comment...


August 22, 2018
Decanted Youth Studio Time Videos

From yesterday:
(youtube link)

And last week:
(youtube link)



Recordings:

Yes, Exactly, Yes! - 1 - Grazing Again -- [5:19]
Yes, Exactly, Yes! - 2 - Tragic Candy -- [7:00]
Yes, Exactly, Yes! - 3 - Marbles/Mumbles -- [6:00]
Yes, Exactly, Yes! - 4 - Toasted Pepitas -- [4:04]
Yes, Exactly, Yes! - 5 - Don't Make Me Think! -- [5:21]
Yes, Exactly, Yes! - 6 - Slippery Enough -- [13:18]
Yes, Exactly, Yes! - 7 - Riot, Anyone? -- [8:52]
Yes, Exactly, Yes! - 8 - Mercury Fur -- [7:50]
Yes, Exactly, Yes! - 9 - Riddle & Salt -- [6:31]
Yes, Exactly, Yes! - 10 - Ghost of an Anchor -- [7:32]

Comment...


July 31, 2018
The Old Man and the C



I was very lucky to play a show last night at Parkside Lounge with Anette, Cory, Andy and Andre, here it is! (I'm on clarinet and occasional vocals, both of which may be difficult to hear).

Update -- the video has me hiding in the shadows:

(youtube link)<

Comment...


April 6, 2018
a quick improvised song

This might be Major Major Major w/ Andy and Sarai (from yesterday's recording)

Side note: my Jams/Full directory now has over 65GB of 192kbps mp3. Apparently that's about 800 hours, or a bit over a month if played continuously. Maybe I should try listening to it all in order.

P.S. I just overhauled brainal.org yay.

1 Comment


April 4, 2018
Show Announcement

NYC readers -- I'm playing with Decanted Youth at Pianos on Tuesday April 17th at 8pm. If you're reading this blog then you probably have an idea of our music, but if not there's a video of a previous show, or you can listen to any of the "rehearsal with sarai and andy" mp3 links throughout.

Recordings:

unexpected need

Comment...


March 20, 2018
share the function names in your mac programs and plug-ins, man

It is amazing to me how many developers shoot themselves in the foot in the name of secrecy, image, embarrassments, fear of piracy, or something else (I have no idea!).

For example, almost all VST plug-in developers I see will never put symbol names in their plug-ins. So when a plug-in crashes, you get crash traces like this:

0   com.somedev.pluginnamethingy    0x00000001a4d351e3 0x1a4cb7000 + 516579
1   libc++abi.dylib                 0x00007fff76d8c3bb __dynamic_cast + 272
2   com.somedev.pluginnamethingy    0x00000001a505d897 0x1a4cb7000 + 3827863
3   com.somedev.pluginnamethingy    0x00000001a5107d85 0x1a4cb7000 + 4525445
4   com.somedev.pluginnamethingy    0x00000001a5107d15 0x1a4cb7000 + 4525333
5   com.somedev.pluginnamethingy    0x00000001a54740ec 0x1a4cb7000 + 8114412
6   com.somedev.pluginnamethingy    0x00000001a5107d85 0x1a4cb7000 + 4525445
7   com.somedev.pluginnamethingy    0x00000001a5145407 0x1a4cb7000 + 4776967
8   com.somedev.pluginnamethingy    0x00000001a5107d15 0x1a4cb7000 + 4525333
9   com.somedev.pluginnamethingy    0x00000001a5273ac1 0x1a4cb7000 + 6015681
10  com.somedev.pluginnamethingy    0x00000001a5103be4 0x1a4cb7000 + 4508644
Super helpful to everybody involved, right?

In REAPER we have for a very long time, on macOS at least, included symbol names. So when there's a crash, we see:
30  com.apple.AppKit                0x00007fff4f2f2ee6 _NSTryRunModal + 100
31  com.apple.AppKit                0x00007fff4ec5dcf9 -[NSApplication runModalForWindow:] + 133
32  com.cockos.reaper               0x0000000100510cad SWELL_DialogBox(SWELL_DialogResourceIndex*, char const*, 
33  com.cockos.reaper               0x000000010016583b __localizeDialog(void*, char const*, HWND__*, 
34  com.cockos.reaper               0x0000000100149a02 LoadProjectFromContext(ReaProject*, ProjectStateContext*, 
35  com.cockos.reaper               0x0000000100147fcc LoadProject(ReaProject*, char const*, int*, int) + 972
36  com.cockos.reaper               0x00000001000aef49 DoProjectLoad(char const*, bool, int) + 537
37  com.cockos.reaper               0x00000001000acc3d Main_openProject(char const*) + 509
38  com.cockos.reaper               0x0000000100070098 Main_OnCommandEx2(int, int, ReaProject*) + 9160
39  com.cockos.reaper               0x00000001000d3a6f Main_OnCommandEx(int, int, ReaProject*) + 31
40  com.cockos.reaper               0x000000010036198b KBD_OnMainActionEx(int, int, int, int, HWND__*, 
Yes, our functions are named terribly. It's much worse than it looks, even, but at a quick glance we (or our troubleshooting user) can quickly see exactly what the hell is going on.

Can one do this on VC builds (without doing full line numbers, obviously)? I forget...

Recordings:

live solo improv: super8 multichannel

3 Comments


July 13, 2017
the lowest end

To follow up on my last article about Linux on the ASUS T100TA, I recently acquired (for about $150) an ASUS C201 Chromebook, with a quad-core (1.8ghz?) ARM processor, 4GB RAM, and a tiny 16GB SSD. This is the first time I've used a Chromebook, and ChromeOS feels not-so-bad. I wish we could target it directly!

...but we can't! At least, not without going through Javascript/WebAssembly/whatever. Having said that, one can put it in developer mode (which isn't difficult but also is sort of a pain in the ass, especially when it prompts you whenever it boots to switch out of developer mode, which if you do will wipe out all of your data, ugh). In developer mode, you can use Crouton to install Linux distributions in a chroot environment (ChromeOS uses a version of the Linux kernel, but then has its own special userland environment that is no fun).

I installed Ubuntu 16.04 (xenial) on my C201, and it is working fine for the most part! It's really too bad there's no easy way to install Ubuntu completely native, rather than having to run it alongside ChromeOS. ChromeOS has great support for the hardware (including sleeping), whereas when you're in the Ubuntu view, it doesn't seem you can sleep. So you have to remember to switch back to ChromeOS before closing the lid.

So I built REAPER on this thing, fun! And I still have a few GB of disk left, amazingly. Found a few bugs in EEL2/ARM when building with gcc5, fixed those (I'm now aware of __attribute__((naked)), and __clear_cache()).

Some interesting performance comparisons, compiling REAPER:

  • C201 (gcc 5.4): 9m 7s
  • T100TA (gcc 6.3): 8m 45s
  • Raspberry Pi 3 w/ slow MicroSD (gcc 4.7): 28m
REAPER v5.50rc6 (48khz, 256 spls, stock settings), "BradSucks_MakingMeNervous.rpp" from old REAPER installers -- OGG Vorbis audio at low samplerates, a few FX here and there, not a whole lot else:
  • C201: 28% CPU, 13% RT CPU, 15% FX CPU, longest block: 1.5ms
  • T100TA: 22% CPU, 9% RT CPU, 10% FX CPU, longest block 0.9ms
(The T100TA's ALSA drivers are rough, can't do samplerates other than 48khz, can't do full duplex...)

Overall both of these cheapo laptops are really quite nice, reasonably usable for things, nice screens, outstanding battery life. If only the C201 could run Linux directly without the ugly ChromeOS developer-mode kludge (and if it had a 64GB SSD instead of 16GB...). Also, I do miss the T100TA's charge-from-microUSB (the C201 has a small 12V power supply, but charging via USB is better even if it is slow).

I'll probably use the T100TA more than the C201 -- not because it's slightly faster, but because I feel like I own it, whereas on the C201 I feel like I'm a guest of Google's (as a side note, apparently you can install a fully native Debian, but I haven't gotten there yet.. The fact that you have to use the kernel blob from ChromeOS makes me hesitate more, but one of these days I might give it a shot).

4 Comments


May 12, 2017
linux hacking on an ASUS T100TA

I've been working on a REAPER linux port for a few years, on and off, but more intensely the last month or two. It's actually coming along nicely, and it's mostly lot of fun (except for getting clipboard/drag-drop working, ugh that sucked ;). Reinventing the world can be fun, surprisingly.

I've also been a bit frustrated with Windows (that crazy defender/antispyware exploit comes to mind, but also one of my Win10 laptops used to update when I didn't want it to, and now won't update when I do), so I decided to install linux on my T100TA. This is a nice little tablet/laptop hybrid which I got for $200, weighs something like 2 pounds, has a quad core Atom Bay Trail CPU, 64GB of MMC flash, 2GB of RAM, feels like a toy, and has a really outstanding battery life (8 hours easily, doing compiling and whatnot). It's not especially fast, I will concede. Also, I cracked my screen, which prevents me from using the multitouch, but other than that it still works well.

Anyway, linux isn't officially supported on this device, which boots via EFI, but following this guide worked on the first try, though I had to use the audio instructions from here. I installed Ubuntu 17.04 x86_64.

I did all of the workarounds listed, and everything seemed to be working well (lack of suspend/hibernate is an obvious shortcoming, but it booted pretty fast), until the random filesystem errors started happening. I figured out that the errors were occurring on read, the most obvious way to test would be to run:

debsums -c
which will check the md5sum for the various files installed by various packages. If I did this with the default configuration, I would get random files failing. Interestingly, I could md5sum huge files and get consistent (correct results). Strange. So I decided to dig through the kernel driver source, for the first time in many many years.

Workaround 1: boot with:
sdhci.debug_quirks=96
This disables DMA/ADMA transfers, forcing all transfers to use PIO. This solved the problem completely, but lowered the transfer rates down to about (a very painful) 5MB/sec. This allowed me to (slowly) compile kernels for testing (which, using the stock ubuntu kernel configuration, meant a few hours to compile the kernel and the tons and tons of drivers used by it, ouch. Also I forgot to turn off debug symbols so it was extra slow).

I tried a lot of things, disabling various features, getting little bits of progress, but what finally ended up fixing it was totally simple. I'm not sure if it's the correct fix, but since I've added it I've done hours of testing and haven't had any failures, so I'm hoping it's good enough. Workaround 2 (I was testing with 4.11.0):
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -2665,6 +2665,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask)
 				 */
 				host->data_early = 1;
 			} else {
+				mdelay(1); // TODO if (host->quirks2 & SDHCI_QUIRK2_SLEEP_AFTER_DMA)
 				sdhci_finish_data(host);
 			}
 		}
Delaying 1ms after each DMA transfer isn't ideal, but typically these transfers are 64k-256k, so it shouldn't cause too many performance issues (changing it to usleep(500) might be worth trying too, but I've recompiled kernel modules and regenerated initrd and rebooted way way too many times these last few days). I still get reads of over 50MB/sec which is fine for my uses.

To be properly added it would need some logic in sdhci-acpi.c to detect the exact chipset/version -- 80860F14:01, not sure how to more-uniquely identify it -- and a new SDHCI_QUIRK2_SLEEP_AFTER_DMA flag in sdhci.h). I'm not sure this is really worth including in the kernel (or indeed if it is even applicable to other T100TAs out there), but if you're finding your disk corrupting on a Bay Trail SDHCI/MMC device, it might help!

6 Comments


February 2, 2017
macOS screen updating, 2017 edition

TL;DR: Retina iMac (4k/5k) owners can greatly improve the graphics performance of many applications (including REAPER) by setting the color profile (in System Preferences, Displays, Color tab) to "Generic RGB" or "Adobe RGB." (and restarting REAPER and/or other applications being tested)

I previously wrote in mid-2014 about the state of blitting bitmaps to screen on modern OS X (now macOS) versions. Since then, Apple has released new hardware (including Retina iMacs) and a couple of new macOS versions.

Much of that article is still useful today, but I made a mistake in the second update:

    OK, if you provide a bitmap that is twice the size of the drawing rect, you can avoid argb32_image_mark_RGBXX, and get the Retina display to update in about 5-7ms, which is a good improvement (but by no means impressive, given how powerful this machine is). I made a very simple software scaler (that turns each pixel into 4), and it uses very little CPU.
While this was helpful (and did decrease the amount of time spent blitting), it was wrong in that the reason for the faster blit was that the system was parallelizing the blit with multiple cores. So, it was faster, but it also used more CPU (and was generally wasteful).

I discovered this because I've been researching how to improve REAPER's graphic performance on the iMac 5k in particular, so I started benchmarking. This time around, I figured I should measure how many screen pixels are updated and divide that by how long it takes. Some results, based on my memory (I'm not going to rerun them for this article, laziness).

Initial version (REAPER 5.32 state, using the retina hack described above, public WDL as of today):
  • old C2D iMac, 10.6: 350MPix/sec
  • mid-2012 RMBP 15", 10.12, Thunderbolt display (non-retina): 1500MPix/sec
  • mid-2012 RMBP 15", 10.12, built-in display (retina): 800MPix/sec
  • late-2015 Retina iMac 5k, 10.12: 192MPix/sec
The one that really jumped out at me was the Retina iMac 5k -- it's a quarter of the speed of the RMBP! WTF. We'll get to that later.

After I realized the hack above was actually doing more work (thank you, Xcode instrumentation), I did some more experiments, avoiding the hack, and found that in the newer SDKs there are kCGImageByteOrderXYZ flags (I don't believe it was in previous SDKs), and found that these alised to KCGBitmapByteOrderXYZ, and that when using kCGBitmapByteOrder32Host with the pixel format for CGImageCreate()/etc, it would speed things up. With retina hack removed:
  • mid-2012 RMBP 15", 10.12, built-in display (retina): 300MPix/sec
  • late-2015 Retina iMac 5k, 10.12: 152MPix/sec
With retina hack removed and byte order set to host:
  • old C2D iMac, 10.6: 350MPix/sec
  • mid-2012 RMBP 15", 10.12, Thunderbolt display (non-retina): 1500MPix/sec
  • mid-2012 RMBP 15", 10.12, built-in display (retina): 720MPix/sec
  • late-2015 Retina iMac 5k, 10.12: 200MPix/sec
The non-retina displays might have changed slightly, but it was insignificant. So, by setting the byte order to native, we get the Retina MBP close to the level of performance of the hack, which isn't great but is serviceable, and at least the CPU use is decreased. This also has the benefit (drawback?) of making the byte-order of pixels the same on macOS/Intel and win32, which will take some more attention (and a lot of testing).

From profiling and looking at the code, this blit performance could easily be improved by Apple -- the inner loop where most time is being spent does a lot more than it needs to. Come on Apple, make us happy. Details offered on request.

Of course, this really doesn't do anything for the iMac 5k -- 200MPix/sec is *TERRIBLE*. The full screen is 15 megapixels, so at most that gets you around 13fps, and that's at 100% CPU use. After some more profiling, I found that the function chewing the most CPU ended in "64". Then it hit me -- was this display running in 16 bits per channel? A quick google search later, it was clear: the Retina iMacs have 10-bit displays, and you can run them in 10 bits per channel, which means 64 bits per pixel. macOS is converting all of our pixels to 64 bits per pixel (I should also mention that it seems to be doing a very slow job of it). Luckily, changing the color profile (in system preferences, displays) to "Generic RGB" or similar disables this, and it gets the ~800MPix/sec level of performance similar to the RMBP, which is at least tolerable.

Sorry for the long wordy mess above, I'm posting it here so that google finds it and anybody looking into why their software is slow on macOS 10.11 or 10.12 on retina imacs have some explanation.

Also please please please Apple optimize CGContextDrawImage()! I'm drawing an image with no alpha channel and no interpolation and no blend mode and the inner loop is checking each pixel to see if the alpha is 255? I mean wtf. You can do better. Hell, you've done way better. All that "new" Retina code needs optimizing!

Update a few hours later:
Fixing various issues with the updated byte-ordering, CoreText produces quite different output for CGBitmapContexts created with different byte orderings:


Hmph! Not sure which one is "correct" there... hmm... If you use kCGImageAlphaPremultipliedFirst for the CGBitmapContext rather than kCGImageAlphaNoneFirst, then it looks closer to the original, maybe. ?

Also other caveat: NSBitmapImageRep can't seem to deal with the ARGB format either, so if you use that you need to manually bswap the pixels...

Update (2019): SolvedWorked around most of this issue by using Metal, read here.

4 Comments


[ older results... ]



Search comments - Ignore HTML tags
search : rss : recent comments : Copyright © 2024 Justin Frankel