Chris Lowis2024-01-01T08:33:45+00:00https://chrislowis.co.ukChris LowisYearnotes 20232024-01-01T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2024/01/01/yearnotes<p>Dealing with my <strong>heart condition</strong> (obstructive hypotrophic cardiomyopathy) has been a big part of my year. I was invited to take part in a randomised controlled trial of a new medicine. This has involved frequent trips to Royal Brompton hospital in Chelsea where I’ve been looked after by a wonderful and talented team of nurses, doctors and researchers. The trial itself was double blinded so we’ll never know which branch of was in.</p>
<p>That said, I felt very good while on the trial and when I abruptly stopped the treatment at the end of the trial I started to feel as bad as I ever have again. Frequent breathlessness and chest pain, and difficulty in handling some everyday tasks (getting the kids to school on time in particular).</p>
<p>I found this emotionally challenging at times, the sense of mortality, of not being able to do some things my kids want me to do and knowing that I’ll never be the same as I was. However I’ve been accepted on to a long term study of the same medicine now and it seems to be working pretty well. I already have a lot more energy and a lot less discomfort.</p>
<p>I also had a genetic test this year and found that I have a “pathogenic variant in MYBPC3”. The upshot of this is that my immediate family may also be affected. For now, there’s nothing I can do about that - but the kids will be screened occasionally at GOSH as they get older and at some point they may have a genetic test too. I have to think through the implications of that.</p>
<p>I’m incredibly lucky to receive the care I do, I’ve been really enjoying being part of the scientific process to develop a new treatment and I’m thankful for the health I do have.</p>
<p>It’s been a pretty successful year <strong>work</strong>-wise. I’ve been consistently busy with projects - first joining a team of <a href="https://www.unthinkabledigital.co.uk/">Unthinkable</a> folks prototyping new things for a university and then with <a href="https://www.dxw.com/">dwx</a> working on various bits of the GOV.UK codebase.</p>
<p>It continues to be a pleasure working with my Go Free Range friends, we’re good at supporting each other through various ups- and downs- and while I think we find it hard to be deliberate about the future, we’re very good at making the most of the present. Having regular days together really helps as I still find working on my own pretty isolating. Having a small office at <a href="https://space4.tech/">Space4</a> works well for me (I still don’t like working from home) and I need to make more of the opportunity to hang out with other people who are based there.</p>
<p>My <strong>family</strong> have been mostly healthy and happy this year. N- moved from Year 1 to Year 2 in September and has gone from carefully sounding out words to independently reading. I love fielding all the questions he has about the things he’s learning. F- has gone from toddler to little boy, he’s speaking a lot, telling “jokes” and is very physically active.</p>
<p>We spent the school summer holidays in Colombia visiting family. I really enjoyed the trip. Compared to the last visit I struggled a lot less being at altitude - likely because the medication I’m taking was working very well. I particularly enjoyed hiring a bike and cycling with N- through the <a href="https://en.wikipedia.org/wiki/Ciclov%C3%ADa">closed streets of Bogotá</a> on Sundays.</p>
<p>I decided not to work on client projects during that time and instead spent some time handling GFR admin tasks and starting work on <a href="https://jam.coop">jam.coop</a>. The latter is an idea we’ve been kicking around since Bandcamp was taken over by Epic games and the management tried to prevent the formation of a workers’ union. We think our <a href="https://jam.coop">co-operatively-owned online record shop</a> is a viable alternative.</p>
<p>At the start of the year I resolved to record and release some of my own <strong>music</strong>. I spent most of my free time in January and February finishing up an <a href="https://chrislowis.co.uk/music#1858">EP of ambient music</a> and later in the year had a track included on <a href="https://jogginghouse.bandcamp.com/album/a-compilation-for-humanitarian-aid">a compilation</a>. The process took a lot out of me creatively and I haven’t had the same dedication since - but I do regularly sit down in the evening to just make sounds and relax.</p>
<p>I’ve enjoyed getting out with friends to <strong>gigs</strong> this year. <a href="https://endoftheroadfestival.com/wp-content/uploads/2023/05/EOTR23-day-splits-web-poster-1080x1350px-1-scaled.jpg">End of the Road festival</a> with my brother and N- was really good fun. I saw <a href="https://www.stilllisteningmagazine.com/features/gig-review-sylvan-esso-at-electric-brixton">Sylvan Esso</a> with Adri in Brixton, <a href="https://www.theguardian.com/music/2023/oct/23/kronos-quartet-five-decades-review-virtuosic-quartet-celebrate-with-djembe-eggplant-and-hendrix">Kronos Quartet</a> with Tom at the Barbican, Hainbach, Look Mum No Computer and Tim Hecker with friends from the internet, Hainbach again at Iklektic with Paul and Pedro, and a few other things I can’t remember right now (this is why we need weeknotes, Chris).</p>
<p>I <strong>read</strong> some actual paperback books in the first half of the year - Ursula K. Le Guin’s <a href="https://en.wikipedia.org/wiki/Hainish_Cycle">Hainish Cycle</a>. A lot of people have recommended these to me over the years. I particularly (and somewhat predictably) enjoyed the anarchist utopia “The Dispossessed” but the whole series was great and reading them back-to-back has meant that the world they created has stuck with me. I enjoyed buying old paperbacks from Camden Market at Joel’s <a href="https://www.ethicalbooksearch.com/uk">Ethical Book Search</a>.</p>
<p>Very little <strong>cinema</strong> this year but Adri and I did take a day off to watch <a href="https://en.wikipedia.org/wiki/Past_Lives_(film)">Past Lives</a> and eat food in Brixton. It’s a wonderful film.</p>
Weeknotes 572023-04-17T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2023/04/17/weeknotes<p>We had a good Easter weekend visiting my parents. Is it possible to eat too much chocolate? The answer is yes. Easter Monday was my birthday but as that largely involved driving back to London, A- and I took the day off on Tuesday and went for a mooch around Soho. Shopping, and eating tasty snacks at <a href="https://www.thecolombiancoffeeco.org/">The Colombian Coffee Company</a>.</p>
<p>Normal service resumed on Wednesday with James, Chris and I tackling our first larger bit of work at GDS. We’ve been trying to make sense of the <a href="https://www.gov.uk/world/embassies">/world/embassies</a> page and how to tease apart the business logic (and a myriad of <a href="https://github.com/alphagov/whitehall/blob/3b5b83ab17a11a695bc893da978b69c04a74bc9d/app/presenters/embassy_presenter.rb#L43">edge cases</a>) from the presentational code to allow both parts to be worked on more easily. This isn’t particularly interesting, I know, but it’s such a wonderful novelty to be able to link to code I’m working on (most of the code that makes up GOV.UK is open-source) that I’m taking the opportunity.</p>
<p>On Thursday I met a friend after work to catch up and see <a href="https://www.lookmumnocomputer.com/">Look Mum No Computer</a> <a href="https://www.youtube.com/watch?v=ovdgo6k4Cdc">play at The Lower Third</a>. Unpredictable home-made synth and stand-up comedy with support from the wonderful <a href="https://www.clementineblueart.com/">Clementine Blue</a>.</p>
<p>My brother was in town on Friday, so I finished work early, had a couple of pints at the Southampton Arms before heading to Hackney EartH for the welcome return of <a href="https://en.wikipedia.org/wiki/Stornoway_(band)">Stornoway</a>. They’re a very uplifting band to see live - lots of warm, funny chat between songs, and usually some kind of audience participation. On Friday this involved a conga line around the venue and a mid-song game of musical statues.</p>
<p>I met a friend to drink coffee and go <a href="https://soundsoftheuniverse.com/info">record</a> shopping for his birthday in Soho (again!) on Saturday morning. Sunday was spent taking the kids swimming, to a soft play and then a family day at <a href="https://www.orabeer.com/en/">ORA Brewing</a>’s tap room in Tottenham. Bouncy-castles and beer, what could go wrong? Surprisingly, no one died.</p>
Weeknotes 562023-04-09T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2023/04/09/weeknotes<p>I spent most of the day on Monday at Royal Brompton hospital receiving my first dose of a new medicine/placebo. I’m taking part in a stage three clinical trial of a new drug for treating obstructive hypertrophic cardiomyopathy. It was an uneventful and slightly drawn out day that culminated in me taking 4 tiny white pills that may, or may not, be the drug. I spent most of the rest of the week trying to guess which branch of the test I’m in. I have no idea! I feel fine I think. Is that good?</p>
<p>On Tuesday I pressed the “publish” button in Bandcamp and <a href="https://undershadedtrees.bandcamp.com">released my first EP</a>. I’d been fiddling with the details of the page for about a week for no good reason and eventually decided to go for it. I don’t really know why it’s so difficult to put creative things out into the world. I had no expectations that anyone would listen, but I did make an effort to promote it via Mastodon, Instagram and a couple of forums. As of today Bandcamp tells me over 200 people have listened and tracks have been played 700 times. It even got a bit of <a href="https://www.100komma7.lu/program/episode/437271/202304082200-202304082300">radio play in Luxembourg</a>. This is much more than I expected and very humbling.</p>
<p>Mostly it’s been touching to hear people have enjoyed it or played it as part of their day to day activities. The good thing about ambient music is that if someone tells you they fell asleep while listening or had it on in the background while they did something else, that’s a strong signal of approval.</p>
<p>I’m already working on ideas (in my head) for my next project and looking forward to spending time making music again.</p>
<p>It was good to write some code and do some pair programming this week, but I’m feeling a bit rusty, it’s been a while since I had to spend all day reading and writing code.</p>
<p>We’re spending the Easter weekend with my parents, the kids have enjoyed Easter egg hunting, a soft play visit, chasing the cat and trying not to break my Dad’s model railway.</p>
Weeknotes 552023-03-31T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2023/03/31/weeknotes<p>I started a new work project properly this week. We’re back at the Government Digital Service working on the publishing platform. It’s been really fun meeting the current developers and doing a little bit of pairing. Their <a href="https://github.com/alphagov/govuk-docker">docker based development environment</a> made getting everything running locally fairly painless. I’m also enjoying having 2-factor authentication via a Yubikey hardware key. When I unplug the key almost all services fail to authenticate, and I am truly disconnected from work.</p>
<p>This week I finished up the data support project with Sports England governing bodies. Over the last 2 months I’ve spoken to data professionals at 8 different SGBs, some on several occasions, and it’s been a joyful and enriching experience.</p>
<p>We celebrated F-‘s birthday on Sunday at the <a href="https://www.qcca.org.uk/peggy-jay-centre">Peggy Jay Centre</a> on Hampstead Heath. A- and I spent two days baking and decorating the cake which F- greeted with studied indifference. It was lovely to have a room full of friends and family though. It wasn’t really possible for us to do this kind of thing for the first couple of years of his life, so I plan to make up for it now.</p>
<p>N-‘s school broke up at lunchtime today for the Easter holiday, so we went to the garish <a href="https://babylonpark.com/index.php/UK_Web_en/">Babylon Park</a> amusement arcade in Camden. The best value-for-money game I’ve found is air-hockey: we get to play against each other and each game lasts for a good 10 minutes or more. Sadly, N- is quite taken with the robot-claw machines which offer a scant 5 seconds of entertainment for the same price. I will impart my Yorkshire-man monetery values upon him if it’s the last thing I do.</p>
<p>I got accepted onto the clinical trial I mentioned in
<a href="/weeknotes/2023/03/22/weeknotes.html">last week’s notes</a>. This followed an exercise capability test at a swanky Harley Street facility last Friday. I’m due to take my first dose next week. If the next time you see me I’m sporting 3 arms and glowing fluorescently, keep it to yourself yeah?</p>
<p>I’ve told some friends that I’ll be releasing an EP of music next week, so am now on the hook to do so.</p>
<p>Late to the party, I took up Tom’s recommendation to watch <a href="https://www.youtube.com/@bobbyfingers">Bobby Fingers</a>. Most of the week has been spent watching Bobby Fingers or telling other people to watch Bobby Fingers. WATCH BOBBY FINGERS.</p>
Weeknotes 542023-03-22T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2023/03/22/weeknotes<p>A fairly quiet week at work as we wait for a large, new project to start. I’m still providing some support/mentoring/coaching to data people with sports governing bodies so spent some time preparing to talk to England Netball about sample sizes and survey data.</p>
<p>N-‘s school was closed for two days due to NEU strike action (✊), and A- agreed to look after him this time, so I took F- on his usual Monday “at home” day. We went to his swimming lesson in the morning, had a cake afterwards then home for lunch and a nap, followed by <a href="https://www.tempototsnorthlondon.co.uk/">Tempo Tots</a> at <a href="https://ktcityfarm.org.uk/">Kentish Town City Farm</a>. Perfect.</p>
<p>LRUG on Monday night was well-attended, I enjoyed both talks and catching up with some ex-FutureLearn colleagues afterwards.</p>
<p>I’ve been invited to take part in a clinical trial for a new drug specifically developed for the <a href="https://www.bhf.org.uk/informationsupport/conditions/cardiomyopathy/hypertrophic-cardiomyopathy">heart condition</a> I have. This is exciting because although I may receive the placebo/<a href="https://www.youtube.com/watch?v=liiVX55tJ7E">killer bees</a> during the trial I should be eligible in any case for the real drug after the trial concludes. I’m definitely not “my old self” so the promise of something that can help that doesn’t involve invasive surgery is welcome. I’ve had the first of a few screening tests to see if I’m eligible and I’m waiting to see if I’ve been accepted.</p>
<p>I’ve sent some music I’ve been working on to a friend who offered to master it for me and I’m now thinking about all the things I’ll need to do (artwork, promotion, streaming services etc) to self-release it. I find it really hard to build up the courage to just let this music go and move on. The book “Art & Fear” (<a href="https://amzn.to/3yYs51F">amazon</a>, <a href="https://www.ethicalbooksearch.com/uk/books/m/ol:OL3936078W-ol:OL34627628W-is:9781800815971/art-and-fear-david-bayles-ted-orland">ethical alternatives</a>) has been a very useful reminder for why I should.</p>
Weeknotes 532022-11-20T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/11/20/weeknotes<p>We needed a “quick” prototype to test with some research participants this week. I wasn’t sure what technology to use - I’ve been favouring static stites with a bit of JavaScript lately, but after staring at the wireframes for a bit and asking #roos for some reckons I decided I’d be better off building some of the trickier parts (persistance, authentication) rather than trying to fake them out.</p>
<p>I started a new Rails 7 project and its been pretty smooth sailing so far. The <a href="https://github.com/mikker/passwordless">passwordless</a> engine gave me a basic authentication system fairly quickly (although faffing with SendGrid and Heroku to send the magic link email took the bulk of the time). I’ve been able to use the rails scaffolds to get started quickly on some of the other forms and pages. It feels good to be writing some code and familiarising myself with some recent Rails features.</p>
<p>F-‘s nursery was closed on Wednesday so we put our wellies on and headed down to London Zoo for a bit. There’s <a href="https://www.zsl.org/zsl-london-zoo/news/london-zoos-tiger-cubs-get-first-health-check">three young Sumatran tiger cubs</a> at the moment and it was wonderful to watch them playing and learning how to hunt with their mother. We also enjoyed an extended stop in the butterfly house. F- really liked looking at the “spotters guide” cards and trying to find each butterfly.</p>
<p>A-‘s parents flew home on Thursday. It was very sad to see them go and the kids have been a bit unsettled since. It’s quite a change of pace when 2 extra adults leave our small flat!</p>
<p>N-‘s been asking a lot of questions about death and dying recently, and getting sad about the concept that other people (or himself) won’t be around for ever. For fairly obvious reasons I don’t have any particularly comforting words of wisdom or insight into what comes next, but it does feel good to have “grown up” conversations with him about these things and share some moments of sadness or reflection. Essentially I boiled it down to: “we’re all going to die, but hopefully not for a long time, and we’ll have some good times while we wait”. Justin Welby’s probably safe in his job for a bit.</p>
<p>Some of N-‘s questions might be prompted by the school’s discussions about Rememberence Day. According to N- “one hundred and sixty soldiers died in the war”, I didn’t have the heart to tell him the horrible truth.</p>
Weeknotes 522022-11-13T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/11/13/weeknotes<p>I travelled to Glasgow this week for a gathering of the <a href="https://www.coops.tech/">Co-operative Technologists</a> network. It was a restorative and fun couple of days. As well as hanging out with some old and new friends I also spent some time writing a couple of <a href="https://wiki.coops.tech/wiki/Main_Page#How_To_Guides">how to guides</a> to get some things out of our collective brains and onto paper. I worked with some colleagues to form a proposal to trial hiring a coordinator for the network and <a href="https://community.coops.tech/t/cotech-fund-update-november-2022/3407">updated the accounts</a>. It was an expertly-organised event from our friends in <a href="https://mediaco-op.net/">media co-op</a> and it reminded me again why I decided to work in this way.</p>
<p>Travelling was made much easier by having A-s parents over to stay for a few weeks. They were able to help out with the kids while I was away, although F- takes very badly to me being away. It was great to see them all when I made it back on Friday night.</p>
<p>I awakened my dormant <a href="https://ruby.social/web/@chrislowis">ruby.social</a> account to join in the exodus from Twitter. I wasn’t a heavy user of twitter, and have long-since unfollowed politicians and news outlets on there, but the vibe of my local instance has reminded me of what twitter used to be like 14(!) years ago when I created my account.</p>
<p>I’m currently working with the folks at <a href="https://unthinkabledigital.co.uk/">Unthinkable</a>. We’re helping a university incorporate some practices from product teams into the design of their new courses. This week that’s mostly involved prototyping a interface for transcribed lectures that allows students to ask each other questions about the content. I’ve been blown away by how accurate the automated transcriptions from <a href="https://openai.com/blog/whisper/">OpenAI’s Whisper</a> are. I worked quite a lot with <a href="https://www.bbc.co.uk/rd/blog/2012-11-the-world-service-archive-prot">automated transcriptions back at BBC R&D</a> 10 years ago and this is really a step-change from what was possible then.</p>
Weeknotes 512022-03-27T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/03/27/weeknotes<p>This week’s <a href="https://songexploder.net/steve-reich">Song Exploder podcast about Different Trains by Steve Reich</a> was wonderful. Hrishikesh Hirway interviews Reich about the 1st movement “America, before the war”. I enjoyed the fun technical details (early use of samplers and “more overdubs than The Beatles”) of course, but Reich’s recollections of his childhood travels between the East and West coast, his descriptions of the melodic character of the voices he used and why he didn’t want to “fix them”, and his explanation of why he chose to talk about the 1st movement instead of either of the other 2 were incredibly moving.</p>
<p>We started a small project with a startup, helping them build a basic data pipeline and some dashboards. I really enjoy this kind of work, I find the process of moving data around and cleaning it up very peaceful and satisying, and it’s great to be able to do a bit of analysis too. As the data gets tidier and more organised the analysis becomes easier and more powerful. I’ve been doing this kind of project for years now, but the tooling available now really makes a lot of things easier. I’m particularly impressed with <a href="https://www.getdbt.com/">dbt</a>’s command line tooling and the way <a href="https://www.fivetran.com/">fivetran</a> just does its thing and keeps out of the way.</p>
<p>On Saturday night I met <a href="https://coruscate.xyz/">some</a> <a href="https://ambalek.bandcamp.com/">internet</a> <a href="https://fieldswefound.bandcamp.com">friends</a> in real life. We went for a pizza and then to see <a href="https://www.cafeoto.co.uk/events/misc-5th-birthday-hainbach/">Hainbach at Cafe OTO</a>. I really enjoyed his set but the best bit was the opportunity to talk to people, make new friends and be out late in that London. I had a chat with Stefan (Hainbach) before the show, and one with Sam (<a href="https://www.lookmumnocomputer.com/">Look Mum no Computer</a>) after. I tried to pluck up the courage to talk to <a href="https://theutopiastrong.bandcamp.com/album/the-utopia-strong">Steve Davis</a>, who was also in the audience, but failed myself, and you.</p>
Weeknotes 502022-03-13T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/03/13/weeknotes<p>The war in Ukraine continues to depress and morbidly grip me in equal measure. I feel as though everything else is pointless while at the same time I want to grab hold of any joy in daily normality and not let go.</p>
<p>F-‘s nursery was closed on Thursday so I took him to the zoo. We laughed, made animal noises and chased each other around.</p>
Weeknotes 492022-02-22T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/02/22/weeknotes<p>Things have been broadly uneventful for a bit, and to be honest, it can continue for a while. After living with the constant threat of a school closing, a kid getting COVID, us getting COVID or some plans being cancelled because of COVID, it’s been nice to have a bit of time where everything has settled into a bit of routine.</p>
<p>We haven’t had much work on in the last week so I’ve been busy trying to drum something up - mostly by working on proposals for small projects with some of our co-op friends.</p>
<p>I donated my workbench to the local <a href="https://menssheds.org.uk/">Men’s shed</a> to make space for a desk to use for music-making, and working from home. It was a bit sad to see the bench I’d built go, but realistically I wasn’t using it to do any meaningful furniture making and I can always build another in the future when the kids are a bit older. So far I’ve loved having a dedicated space to make music with everything plugged in and ready to go.</p>
<p>I tidied up some Supercollider code from some of my musical experiments in January and turned it into a <a href="https://llllllll.co/t/triangles/52662">script for norns</a>. It’s been lovely to see a few other people using it to make things too.</p>
<p>Went to <a href="https://www.arsenal.com/fixture/arsenal/2022-Feb-19/brentford-fc#!match-news">the football</a> on Saturday for the first time in a long time. Overdid the festivities a bit, but it was fun to be back. There’s quite a positive atmosphere there at the moment which makes a difference.</p>
Weeknotes 482022-01-30T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/01/30/weeknotes<p>A fairly quiet week work-wise, which was helpful given that N-‘s after-school club is still closed. We thought we had arranged work for the next 3 months, but unfortunately it’s been scaled back to 2 weeks. Still, it’ll be good to get stuck into something. I enjoyed revisiting old projects and writing supporting evidence for our R&D tax credits claim, it felt good to reflect on what we’d achieved as its easy to miss doing that at the time.</p>
<p>I spent the day with my brother on Monday, met Tom for lunch on Wednesday and had my parents over on Saturday - these all really helped to lift my mood.</p>
<p>Tuesday was a bit of a write-off, I had a scheduled MRI scan with the cardiology team at St. Thomas’s late in the afternoon. I’d been told not to drink caffeine during the day, which meant by lunchtime I had a headache. The scan itself went fine, but I had forgotten how uncomfortable it gets after about half an hour of confinement. I was ready to come out when it finished (about 50mins total).</p>
<p>I made a jam for #jamuary almost every day this week. It feels good to have the creative habit. I’m quite pleased with <a href="/sketches/20220126">number 15</a> and <a href="/sketches/20220130">18</a> and had fun <a href="/sketches/20220124">recording some acoustic guitar</a> on Monday morning.</p>
Weeknotes 472022-01-23T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/01/23/weeknotes<p>It’s fair to say that adjusting back to life in London this week has been pretty hard. N-‘s after school club closed on his first day back for two weeks (due to too many cases of Covid in his year), so we’ve had to adjust our working arrangements already. F- made it through his first week back at nursery, but became ill on Friday afternoon. A LFT says its not Covid, so hopefully just teething or another virus. He’s lost his voice!</p>
<p>We really miss family and sunshine. The feeling of loneliness is compounded by the fact that our closest friends in London are now serious about leaving the city and have put their flat on the market.</p>
<p>On the bright side, <a href="https://gofreerange.com">we’ve</a> got some work lined up with a new client and its been fun catching up with <a href="https://jamesmead.org/">James</a> this week and working on our R&D tax credit application together (don’t say we don’t know how to party). It’ll be good to get stuck into some client work again, and hopefully get back into Space4.</p>
<p>I also created <a href="http://localhost:4000/sketches/">5 “jams”</a> for jamuary, had a big sort out of a book case, hung out with F- and then went for lunch at a friend’s place.</p>
<p>I’ve lost a bit over 2kg so far this year. Ambitiously, I’d like to lose 10kg by my birthday in April.</p>
Weeknotes 462022-01-16T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/01/16/weeknotes<p>We arrived home. The trip to Barranquilla was relaxing, we had an impromptu extra night in the city itself because we missed our flight back to Bogotá (3am != 3pm, for reference). The last few days in Bogotá were fun, N- had a “sleep over” at his cousin’s house, and we caught up with the extended family a bit. We’d arranged an evening out with one of A-‘s school friends but unfortunately they’d caught COVID and had to postpone. There wasn’t much sign of the new varient when we arrived, but I suspect it’s a matter of time.</p>
<p>The flight back to London went fairly well. N- slept almost the whole of the first 10 hour leg. F- slept a reasonable amount but mostly on top of me which meant I didn’t get much rest. The connection in Madrid was mercifully short and stress-free and we had a taxi waiting for us in Heathrow. We isolated at home for 2 days and have been able to go out today after our (paid-for) LFTs were negative.</p>
<p>We’ve binged the first 6 episodes of <a href="https://en.wikipedia.org/wiki/Cobra_Kai">Cobra Kai</a> over the (jetlagged) weekend. I was nervous that it wouldn’t live up to the high artistic bar set by the previous series, but after a slow start a spectacular training montage in episode 5 has redeemed it. I’m going to savour the final four episodes like a fine wine.</p>
<p>I’ve made a few bits of music for <a href="https://disquiet.com/2022/01/07/jamuary-is-happening/">#jamuary</a>. So far, they’ve all been an excuse for me to learn more about SuperCollider and in particular its pattern language for sequencing musical events. Rather than stressing myself out coming up with videos to go along with each one for Instagram, I’ve instead been dumping the mp3s and some notes in the <a href="/sketches">sketches</a> category on this site. I’ve been sharing them in a private discord server I’m a member of which means I don’t miss out on the hashtag-driven community aspect of the project too much.</p>
Weeknotes 452022-01-02T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2022/01/02/weeknotes<p>We’ve travelled to Barranquilla for a few days by the sea. It’s around an hour flight from Bogotá and F- fell asleep before we took off and woke up after we’d landed. We’re staying in a small resort that seems to specialise in watersports. I’ll know more when I’ve had chance to explore tomorrow.</p>
<p>We saw New Year in at my brother and sister in-laws house, a few extended family joined us too. It was a nice, quiet affair (to be honest, with F- typically waking up at 5am on a good day, I probably wouldn’t have stayed up past midnight at home).</p>
<p>I’ve deployed a few changes to this site that I’ve been hacking on over the last few weeks. I’ve added a section for <a href="/notes">notes</a> to see if it helps me write more without the pressure of “publishing”. I’ve also roughly hacked something together to hold my musical <a href="/sketches">sketches</a>. I’m hoping to create a few <a href="/notes/Supercollider">SuperCollider</a> things for #jamuary.</p>
<p>It’s been a vintage week for Weeknotes. Despite their protestations I’ve really enjoyed reading <a href="https://tomstu.art/yearnotes-1-autopilot">Tom</a>, <a href="https://www.minor9th.com/2021/12/weeknotes-2021-59-wrapping-the-year/">Simon</a> and <a href="https://mudge.name/2022/01/02/2021-yearnotes/">Paul</a>’s yearnotes. I’ve not managed to write every week this year, but I am glad I’ve managed to keep writing them. If nothing else, it’s my offering to a collective practice that gives me a huge amount in return.</p>
Weeknotes 442021-12-27T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/12/27/weeknotes<p>We spent Christmas week in <a href="https://en.wikipedia.org/wiki/Villa_de_Leyva">Villa de Leyva</a> a beautiful old town about 3 hours drive from Bogotá. We rented a house on the edge of the town, and the boys loved playing on the provided trampoline with anyone who would join them.</p>
<p>The <a href="http://granvalle.com/">Parque Gondava</a> dinosaur park was a hit with the kids. Lots of life-size model dinosaurs spread around a lake. Including an animatronic T-rex and a Brachiosaurus you could climb inside.</p>
<p>On Boxing day we visited the <a href="https://laperiquera.com/laperiquera/">Cascadas La Periquera</a> waterfalls. It was a fairly gentle hike down the valley and back up again, but the first time N- had done something like that. He did really well. We stopped for lunch at a <a href="https://goo.gl/maps/8tsTKwxqE5Svcfyq6">fun Italian restaurant</a> nearby.</p>
<p>Merry Christmas and a happy New Year!</p>
Weeknotes 432021-12-19T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/12/19/weeknotes<p>Altitude sickness hit me harder than it has done in the past this week. I started to feel more normal today, a week after we arrived in <a href="/notes/bogota.html">Bogotá</a> (2,640m). The kids took a few days to adjust to the time zone change too, meaning some broken nights sleep and early starts. But some bright, sunny weather helped a lot with the jetlag.</p>
<p>My brother-in-law’s family have a new dog. A beautiful <a href="https://en.wikipedia.org/wiki/Australian_Shepherd">Australian Shepherd</a> called Bucky. N- is scared of dogs generally, having had a run in with a couple when he was younger (and projecting a lot of anxiety during the 1st lockdown at dogs). The family have been brilliant though, introducing him very slowly and showing him how to ask the dog to sit and stay still. While he’s still not picked up the courage to actively play with Bucky, N- spent this afternoon in the same room together and was a lot less on edge than earlier in the week. It’s really positive.</p>
<p>We went to the city branch of famous Colombian steakhouse <a href="https://www.andrescarnederes.com/">Andres</a> yesterday to meet one of A-‘s university friends. We never got around to ordering any steak having filled up on cheese-covered <a href="https://www.mycolombianrecipes.com/fried-green-plantains-patacones/">patacones</a> and snacks. While not as extensive as the massive ranch-style original, the Bogotá branch of Andres still has a lot of activities for children. F- enjoyed painting, while N- made popcorn, painted his nails and did some woodwork.</p>
<p>There’s not a lot of wildlife to be seen in the city, but I did spot a small hummingbird drinking from a flower outside A-‘s parents window.</p>
Weeknotes 422021-12-13T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/12/13/weeknotes<p>We arrived in Colombia for a family visit. It took around 24 hours door-to-door yesterday (with a 4am start). To be honest, I’ve been dreading the journey, particularly because F- is at the age where being stuck anywhere for too long drives him a bit mad. I’m not going to say it was an easy journey, but the two boys were <em>great</em>. The only time F- cried was when he couldn’t get his big plastic dinosaur into a small plastic box. Otherwise he travelled really well.</p>
<p>It was exhausting keeping him entertained particularly towards the end when we were tired ourselves, but a few things helped. We wrapped up some little toys in wrapping paper (and kept aside a few gifts from friends to unwrap too). Bringing one of those out every half hour or so was fun. Lots of snacks (airline food is generally bad and Avianca seem to be cost-cutting hard in these pandemic times). We got quite a lot of milage out of a reusable sticker book too.</p>
<p>N- enjoyed some games of magnetic tick-tack-toe and a spot-the-difference book. I also loaded a tablet with some of his favourite programmes<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> and that kept him busy of course.</p>
<p>The jetlag and altitude sickness has hit us pretty hard, but the sun’s shining and the <a href="https://www.spanishdict.com/translate/abuelitos">abuelitos</a> are <em>so happy</em> so it feels like mission accomplished.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>I recently rescued an older Lenovo android tablet from a pile of abandoned computers in my office. It runs an older version of android, but has an SD card reader and runs VLC fine. It turns out you can use <a href="https://github.com/ytdl-org/youtube-dl">youtube-dl</a> to download programes from the iplayer. I used the <a href="https://github.com/kainem/linkgopher">link extractor</a> firefox extension to create text files of episode links, and then the <code class="language-plaintext highlighter-rouge">-a</code> flag to <code class="language-plaintext highlighter-rouge">youtube-dl</code> to download them in batches. The resultant files were quite large (around 500MB for an episode of Hey Duggee) so I ran them through ffmpeg with the <code class="language-plaintext highlighter-rouge">-preset veryfast -vcodec libx264 -crf 28</code> flags to reencode them with a lower bitrate. This made the files about 10 times smaller and the quality was fine. It made it easier to move them and store them all on an SD card. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Weeknotes 412021-12-05T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/12/05/weeknotes<p>We went on a suprisingly smooth Sunday train ride to Chertsey today to meet some friends for a “mini Christmas” before we (hopefully) travel to Colombia for Christmas next weekend. The <a href="https://www.goldengrovestannshill.com/">pub we chose</a> met the criteria of being roughly equidistant from the three families, having more than one choice of veggie/vegan food and having a garden for the kids to play in. The log fire and Christmas tree was a nice bonus.</p>
<p>I spent the week <a href="https://www.agilealliance.org/glossary/mob-programming/">remote mobbing</a> with my GFR collegues. We had a short project with the <a href="https://daylight.academy/">Daylight Academy</a> via a University of Oxford researcher we’ve worked with in the past. It was fun to build a client-side only prototype (in React). We deployed to Netlify which has a <a href="https://docs.netlify.com/visitor-access/oauth-provider-tokens/">handy built-in bit of “serverless” code</a> for doing the OAuth dance with Github. So we were able to build an app that lets users create Github repositories and add files to them all from a “static” site. Feels a bit like magic.</p>
<p>I shared some more of my music-making on <a href="https://www.instagram.com/chris.eats.tapes/">Instagram</a> this week. I rigged up a Heath Robinson wood-and-metal contraption to hold my digital camera so I could shoot overhead video. I find Instagram quite baffling and the constant ads, weird recommedations and Facebook ownership are quite unpleasant. But I’ve also had some really fun and helpful conversations about music on there too, which I think I’d lose if/when I go full <a href="https://indieweb.org/">IndieWeb</a>.</p>
Weeknotes 402021-09-26T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/09/26/weeknotes<p>N- finished his second full week at his new school. It’s been a really big change for him and us, but he’s settling in well and (as far as I can tell from his minimal dispatches) enjoying it. We’re still not into a routine because the drop off and pick up times are very different to before. Also F- returned to nursery, got ill and, yada-yada-yada (cf. every previous f–king weeknote).</p>
<p>I took Thursday and Friday away from client work to attend the CoTech gathering at Space4 in Finsbury Park (where I have an office). It was really comforting and helpful to just spend some time with some other people who’ve been through the last 18 months and decompress. We had a couple of good sessions on working together and developing business, and also a few pints in the pub.</p>
<p>The <a href="https://codeclubworld.org/">thing we’ve been working on</a> with the Raspberry Pi foundation got a wider release this week (with a switch away from a <code class="language-plaintext highlighter-rouge">beta.</code> subdomain). I’m going to talk more about that on the GFR blog soon. Recently I’ve been doing a bit of sound design for the <a href="https://codeclubworld.org/activities/music/a9ee17b3-e043-46f2-9ebd-fbbac79ac7e1">music activity</a> which has been a welcome change and a lot of fun.</p>
<p>I finished up <a href="https://soundcloud.com/chrislowis/my-lifes-work">a track</a> that seemed good enough to share (a lot of them are not good enough) and started another. I’ve been sharing work-in-progress music with a friend of mine, and it’s been really useful and motivating.</p>
<p>It’s our anniversary tomorrow and I get to celebrate spending 7 years married to the most amazing person I know. We’re going to try and find some Greek food for lunch.</p>
Weeknotes 392021-08-30T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/08/30/weeknotes<p>I’ve been on holiday for the last two weeks. First a week in Norfolk and then a week with my parents in Berkshire.</p>
<p>We arrived in Norwich too early to check in to our accomodation. Fortunately everyone’s favourite plaster cast dinosaur <a href="https://en.wikipedia.org/wiki/Dippy_(London)">Dippy</a> was visiting Norwich cathedral so we paid it a visit. Dippy’s visit meant that the city also had a <a href="https://www.break-charity.org/meet-the-t-rex/">Meet the T-rex</a> trail, and N- loved walking around the city ticking off all of the T-Rex’s on his map (it also meant we had an excuse to see the city ourselves). We saw “Afternoon Tea Rex”, “Hulkasaurus” and “T Pot”. I pity the fool that missed the opportunity to design a “Mr. T Rex”.</p>
<p>The weather improved for the second half of the week, so we made a trip to Great Yarmouth. It’s probably the definition of a town that has “seen better days”. I was quite excited to take N- to the arcades and play some games - while less smokey than I remember as a kid, these arcades now seem to have massive fixed-odds slot machine rooms in the back and watching some despondent looking folks shuffle in and out as we played was a bit depressing. We did however have an excellent, great value lunch (including a massive bowl of clams) at Portugese restaurant <a href="https://www.tripadvisor.co.uk/Restaurant_Review-g190788-d19071787-Reviews-A_Varina_Petisqueira_Bar-Great_Yarmouth_Norfolk_East_Anglia_England.html">A Varina Petisqueira</a>.</p>
<p>A day trip to “curious treehouse adventure” <a href="https://www.bewilderwood.co.uk/">BeWILDerwood</a> on Thursday was an unexpected highlight. N- loved the zip wires, slides and massive treehouses (including a huge tree “maze”), F- and I built “dens” by dragging sticks around. It was a suprisingly peaceful place, with a story-time stage, a cafe and really friendly staff.</p>
<p>Cromer was a much nicer seaside town. We visited the small South American zoo <a href="https://amazonazoo.co.uk/">Amazona</a> in the morning and went to the beach in the afternoon. N- immediately made some friends on the beach and spent a couple of hours chasing waves backwards and forwards. F- and I had a nap.</p>
<p>Back at my parents A- had to work again for a few days, but I’d taken the week off. I was able to take a couple of trips out with my parents and the kids (most notably to the magnificant <a href="https://www.rhs.org.uk/gardens/wisley">RHS Wisley</a> gardens) but mostly we spent some quality time with the grandparents at home. My Dad and N- had several happy hours building LEGO city models at the dining room table, my Mum had fun with F- in the garden, and I got to spend some time with my Dad in his workshop making a <a href="https://blog.chrislowis.co.uk/2020/08/16/weeknotes.html">second chair</a>.</p>
Weeknotes 382021-08-16T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/08/16/weeknotes<p>We’re currently camping in Norfolk. This is our first time camping with the kids and we started panicking a bit in the week when some <a href="https://www.bristolpost.co.uk/living-hell-cotswolds-glamping-company-5755075">very unfair</a> coverage appeared in the press. Turns out the internet is wrong and it’s fine. We’ve had a great time, there’s been enough to do to keep N- entertained (unlimited bouncy castles) and it’s a safe environment for F- to run around in. They pitched the tent for us and it’s massive. I bought a mini keg of Adnams Ghost Ship. Several boxes ticked.</p>
<p>Before arriving on Friday we’d spent the week with my parents. It was amazing to see them have a decent amount of time with the kids after the last year and a half. A- and I were also trying to work (me from 7am-1pm and A- from 1pm to 6pm). I love being able to get an early start as I’m much more productive in the morning, but it meant the days were long and tiring.</p>
<p>On to Norwich this morning. We’re staying in an Airbnb in the city centre and hoping to make some trips to the coast.</p>
Weeknotes 372021-08-09T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/08/09/weeknotes<p>School’s out for summer. Unfortunately N- missed his last day at his nursery, because F- had a temperature the day before and they both got sent home pending a PCR test. Since then A- and I have been taking turns looking after them. A trip to the Zoo on Monday and a trip bowling on Friday. F- is so fast on his feet now that both of these activities were exhausting, but also fun.</p>
<p>I’ve been battling a heavy cold (thanks kids) for a couple of weeks. My running has really taken a hit and I’ve been missing it. I tried to run on Wednesday and had to stop, but managed 3km on Saturday. Hopefully on the mend now.</p>
<p>We’ve travelled to my parents for a week so A- and I can work a bit before we go on holiday to Norfolk next week.</p>
<p>When I’ve had a bit of free time I’ve been spending it learning how to use <a href="https://www.reaper.fm/">Reaper</a> and writing music. I’ve been trying to finish songs as best as I can, to get practice at all of the different parts of the process. I’m considering finding a tutor or mentor in the autumn, because I think I’d learn faster with some regular feedback and deadlines.</p>
Weeknotes 362021-07-02T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/07/02/weeknotes<p>I’m fairly settled into a routine of three days in the office and two at home now. It’s working well, I really enjoy having a few more people around even if I’m not directly working with them. I caught up with some co-op friends on Thursday as Outlandish were having a bit of a get-together in the park.</p>
<p>I’ve also been fitting some running into the schedule. A run home from the nursery drop-off on Monday, from the office on Wednesday and round Finsbury park today. I’ve been averaging about 16km a week for the last 3 weeks which is a step up. The Garmin Coach is working out quite well, it gives me a bit of variety and some motivating stats and graphs.</p>
<p>I started making a <a href="https://github.com/chrislo/thunk/blob/main/lib/Engine_Thunk.sc">custom supercollider engine</a> for my long-running <a href="/notes/thunk">norns project</a>. Supercollider is a bit esoteric but a lot of fun. I’m fairly close to releasing a first version. I don’t expect anyone else to use it, but it’s useful to have the idea of shipping something in mind to help me to decide what to work on next.</p>
<p>The boys’ nursery is closed in August so we’ve been busy planning what to do for the month. A- and I are going to split the parenting by working slightly shorter hours and starting or finishing later. We’ll do about of that from my parents, which I’m really looking forward to. We’ve also booked a few nights in Norwich and a few camping (or <a href="https://cloudnineglamping.com/">glamping</a>?) in Norfolk. It’s expensive and booked up everywhere but somehow not having to pay the nursery fees for a month makes it feel slightly more affordable.</p>
<p>I miss my GFR colleagues, I’m really hoping we can get together soon.</p>
Weeknotes 352021-06-13T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/06/13/weeknotes<p>Took the kids to play in the fountains at Granary Square today. For me, it’s better than being on a beach - they get to splash around, but there’s also shade and a big supermarket. I remember taking N- when he was the age F- is now, and it took us a long time to convince him to dip a toe in the water. F- was straight in there although he did get cold fairly quickly. The nursery is closed on Friday for a training day, so I might take them both their again if the weather is good - I imagine it’ll be a lot quieter than it was today.</p>
<p>We’ve been watching <a href="https://www.imdb.com/title/tt4179452/">The Last Kingdom</a>. It’s not an overly complicated story, but like most TV it’s a bit much for my brain. I really appreciate how each episode opens with some drawn out exposition. I generally have no recollection of what happened in the last episode, so this helps a lot.</p>
<p><a href="https://cancel.fm/ripcord/">Ripcord</a> is an alternative client for Slack and Discord. It’s primary advertised feature is “not made from a web browser”. Might be a keeper.</p>
Weeknotes 342021-06-01T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/06/01/weeknotes<p>Another week of childcare disruptions. N- had a temperature on Wednesday lunchtime, so the nursery sent him and F- home pending a coronavirus PCR test. I was able to book a test at a “mobile test centre” in Kentish Town. This turned out to be a white van parked in a carpark near the Forum. There was no tables, chairs or hand sanitiser, but after several frustrating attempts we managed to take a valid test. High fives all round (so much for social distancing) and we had a negative result by the next morning. N- was perfectly fine after a nap and recovered from whatever he had, but unfortunately F- was pretty grumpy and snotty for the rest of the week. I spent two days at home with him while he felt sorry for himself and slept on top of me.</p>
<p>By Sunday both boys were back on form, so yesterday we decided to take a sponteneous trip to Brighton (along with the rest of London, it seemed). I haven’t seen that many people in one place for a long time. F- has barely left North London in his short life, so it was great to see his eyes wide open at the sight of a fairground ride, or a beach full of stones to eat.</p>
<p>I’m into my final week of Couch to 5k although I stuggled with the heat on my run this morning. I’ve heard good things about the <a href="https://connect.garmin.com/features/coach/">Garmin Coach</a> system from a couple of friends and enjoy being told what to do on runs by a machine, so I’m thinking of buying a “Forerunner 45” to give me some structure after the end of the C25K plan.</p>
Weeknotes 332021-05-23T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/05/23/weeknotes<p>N-‘s language skills are now at the <em>bants</em> level. “F- keeps pulling my hair, daddy! If he keeps doing that, I’ll have no hair. Like you!”. And (after watching me give some money to a rather dishevelled looking gentleman on the overground) “Is that your Dad?”.</p>
<p>I appreciate the previous paragraph is in the “kids say the funniest things” genre. What can I say? Slow news week.</p>
<p>The Mirror <a href="https://www.mirror.co.uk/news/uk-news/brit-punters-need-drink-124-24116194">reported</a> “Brit punters will ‘need to drink 124 pints each’ in order to save UK pubs after lockdown”. After rising early on Monday to put on my drinking trousers, a friend pointed out that this figure was based on the additional spending required in the <em>next 12 months</em>. I still had a couple this week, I’m not going to lie.</p>
<p>I’m in week 8 of the <a href="https://www.nhs.uk/live-well/exercise/couch-to-5k-week-by-week/">couch to 5k</a> plan. I had a good run on Wednesday, felt like I’d set off a bit quick but managed to keep the pace until the end. I haven’t managed to repeat that this week, but as Michael Johnson keeps telling me (through the app, he’s not my mate), it’s about the distance not the speed.</p>
<p>I bought a new pair of shoes (online, natch) a couple of weeks ago. I’ve been limping around waiting for the right one to soften ever since. Yesterday I realised there was a big bit of plastic inside the toe that I hadn’t removed.</p>
<p><a href="https://www.bbc.co.uk/programmes/m0002wsm">Nae Pasaran!</a>, the documentary about Glaswegian Rolls Royce workers who refused to service engines for Pinochet’s jets, is on iplayer for the next 18 days (if the BBC survives that long). It’s great.</p>
Weeknotes 322021-05-03T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/05/03/weeknotes<p>F- is teething. Which means he gets a fever quite often. Which means we get asked to collect him from nursery. Which means he needs to have a negative PCR test before he can go back. The nearest walk in test centre is now 3 miles away, and you’re asked to walk or drive. He did not enjoy over an hour of being pushed in the push chair and then having a swab stuffed up his nose. The test was negative.</p>
<p>We also went through this same routine with N- on Monday.</p>
<p>I had my second vaccine shot on Wednesday. A lot less pain in the arm this time, but developed fever and a sore throat after 24 hours. Glad to be doing my bit to stop transmission, and the folks at the St Thomas vaccination centre were typically wonderful.</p>
<p>I was on leave this week. In between all of the above I did manage to finally paint the chair I made, and also the bench stool I brought home from my class with Chris Schwarz 18 months ago. I like painted furniture, but I don’t like painting furniture. I used some black milk paint and finished with a top coat of Osmo.</p>
<p><img src="/images/2021-05-03-painted-chair.jpeg" alt="Painted chair" /></p>
<p>I’ve played <a href="https://awonandphoniks.com/album/nothing-less">Nothing Less</a> by Awon & Phoniks so much since it came out last month. If you like jazzy, sampled, Golden Era boom-bap you will love this.</p>
<p>Back to work on Tuesday. As much as I’m enjoying the current project, I could really do with a proper break. When the kids leave home perhaps?</p>
Weeknotes 312021-04-18T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/04/18/weeknotes<p>My brother came to visit on Friday. We haven’t seen each other since August, which is also more than half of F-‘s life. We had a pint in the beer garden at <a href="http://thesouthamptonarms.co.uk/">The Southampton Arms</a> then picked the boys up from nursery for a very excitable trip to the playground, before heading out for a couple more beers. It was great.</p>
<p>A- is watching Twin Peaks and while I’m trying not to get sucked into another TV Series I am enjoying <a href="https://en.wikipedia.org/wiki/Music_of_Twin_Peaks">the soundtrack</a> in the background, it’s a banger.</p>
<p>We’re getting ready to launch the thing we’ve been working on for the Raspberry Pi Foundation - it’s been a fun but busy week tidying up a lot of loose ends.</p>
<p><a href="https://mudge.name/2021/04/18/weeknotes-77/">Paul recommended</a> the post <a href="https://jacobian.org/2021/apr/7/embrace-the-grind/">Embrace the Grind</a> about how sometimes doing large amounts of tedious work produces results that are magical. Some of the most inspiring people I’ve worked with share that trait in common, but I hadn’t seen it expressed in such a clear way before.</p>
Weeknotes 302021-04-11T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/04/11/weeknotes<p>I celebrated my birthday yesterday. I was able to do more than last year, but things are still quite restricted. Nevertheless, we had a round of crazy golf <a href="https://www.puttinthepark.com/courses/acton-park/">in Acton Park</a> with my friend Dan and his family. We picked up a couple of pizzas from a local pizza place too. Good times.</p>
<p>As well as some delicious birthday beers, I got another nice surprise in the post this week:</p>
<p><img src="/images/w3c_certificate.jpg" alt="Certificate from W3C" /></p>
<p>The <a href="https://www.w3.org/TR/webaudio/">Web Audio API</a> was reached “candidate recommendation” status in January so I think the W3C have been thanking everyone who was involved along the way. I was really touched to receive this certificate, especially given the small part I played in the whole thing - it was a very nice gesture.</p>
<p>We’ve been busy getting the thing we’re making with the Raspberry Pi Foundation ready to share more widely. We’re aiming to invite some people to use it on the 20th April and having a deadline has focussed our prioritisation a bit. It’ll be great to be able to talk about it in more concrete terms.</p>
<p>I spent a couple of evenings adding some more features to <a href="/notes/thunk">thunk</a> my <a href="/notes/norns">norns</a> sequencer. I’ve been able to reuse some of the patterns I developed working <a href="https://github.com/chrislo/brain">on a Rust sequencer</a> last year, and norns has some useful building blocks (e.g. MIDI grid i/o, a <a href="https://github.com/markwheeler/timber/">sampler library</a>) that means things are going a bit faster. I implemented <a href="https://www.attackmagazine.com/technique/passing-notes/daw-drum-machine-swing/">swing</a> and allowed tracks to have up to 64 steps, and I’m already able to use that to make some longer and more interesting-sounding rhythms.</p>
<p>I’m looking forward to seeing my brother on Friday for the first time since August. We might even have a pint outside a pub.</p>
Weeknotes 292021-04-05T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/04/05/weeknotes<p>It was F-‘s 1st birthday on Friday. For obvious reasons his first year has been very different to how we’d planned and imagined while A- was pregnant. But the important things are he’s healthy, happy and makes everything better. Happy birthday little chap.</p>
<p>We used our new-found freedom to celebrate with friends and family over the long weekend. Unfortunately not as one large party but rather 3 separate little picnics. It’s been over 6 months, more than half F-‘s life since my parents saw him. We visited them on Sunday and it was wonderful.</p>
<p>Whenever F- is upset looking at photos of people always seems to calm him down, so we made him a picture book with some of his favourite photos. We got it printed as a baby-friendly <a href="https://www.photobox.co.uk/shop/photo-books/my-first-photo-book">board book</a> with captions in English and, so he can take one to nursery, in Spanish too. We also gave him a <a href="https://en.m.wikipedia.org/wiki/Steel_tongue_drum">small steel tongue drum</a>. This seems like a good instrument for his age. It responds well to being bashed with hands or the little beaters it comes with. And as it’s tuned with a pentatonic scale it sounds fine no matter which keys you hit together.</p>
<p>I spent most of the week at work working with CSS. I really love this stage of a project, starting to polish things up ready to share them and making a prototype feel more like a “real thing”. Doing CSS work also takes me back to the feeling I had when first building websites in the 90s, making a change to the code and seeing a visual result. Just with more indirection and compilers. Seriously though, SCSS and the module style we’re using for this project do make things much easier and more immediate. But trying to do things like applying an inset shadow on a single side of a <code class="language-plaintext highlighter-rouge">div</code> makes me realise why they call them <a href="https://css-tricks.com/">CSS tricks</a>.</p>
<p>I met <a href="https://www.makingplans.net/">Nigel</a> for a walk on Wednesday night. We took a growler of beer from The Southampton Arms up to the heath and had a good natter. Happy days.</p>
Weeknotes 282021-03-23T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/03/23/weeknotes<ul>
<li>I had the most enjoyable week at work I can remember for a long time. We can’t talk about what we’re working on just yet, but last week I got to build something that combines Web Audio, computer music, programming languages and education. And to do that with a brilliant team. I was in my element!</li>
<li>F- had his first full day at nursery on Friday. He seems to be settling in well. I miss him being around during the day though. And as is customary he’s immediately caught a bunging cold.</li>
<li>Currently reading “Fear of music: why people get Rothko but don’t get Stockhausen” by David Stubbs. As well as trying to answer the central question about why “modern art” is so popular but the equivalent music is not, it also has a great art/music history of the 20th century.</li>
<li>Predictably my music making has taken a bit of a back seat in favour of <a href="/notes/thunk">music software making</a>. But I really enjoy all aspects of this hobby.</li>
<li>I went for a run. It was so hard, being ill and in the house for months really takes it out of you it turns out. I thought I’d try 3km at 7:30/km which was a fairly easy pace and distance for me a year ago. I managed about 1km. Strava, while generally pretty awful, has two things going for it. Firstly, it continues to insist on calling me an athlete which amuses me no end. And secondly, it reminded me that with some effort I was able to improve and run a 5k last year.</li>
<li>I enjoyed Beer Hawk’s <a href="https://onewayroadtobeer.com/">One way road to beer</a> data visualisation. Or, as my friend dryly put it, 140 days until “unexpected circumstances” shut everything again. Having said that, have bike, will travel. If anyone fancies a beer in the park, give me a shout.</li>
</ul>
Weeknotes 272021-03-15T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/03/15/weeknotes<ul>
<li>It was F-‘s first day at nursery on Wednesday. He’ll be going to the same Spanish-speaking nursery as his older brother 3 days a week. The settling in routine is a bit different this time around given the covid restrictions but A- was able to spend some time with him getting him used to the surroundings. By Friday he spent a few hours, including a nap and lunch. He seemed happy to be left there - on the one hand you want them to settle, but on the other it’d be quite nice if they cared when you left. He’ll have a 4 day break before his next visit so next week could be worse.</li>
<li>I had a spirit-lifting virtual cup of tea with <a href="http://www.h-lame.com/">Murray</a> this week. For various reasons, I was in the office for the day and it was great to be able to a large monitor, decent camera and fast connection for the chat. Not the same as in-person, but much better than at home.</li>
<li>I’ve been doing a reasonable amount of business development (calls with potential clients) and account management (calls with current clients) recently. I’ve found myself enjoying and looking forward to this kind of work, and I like that it is largely self-directed. Maybe I’ll end up doing more of it?</li>
<li>I <a href="https://github.com/schollz/barcode/pull/13">made my first change to a norns script</a>. So far it’s been a fun environment to work in. Norns is effectively a custom Linux distribution running on a raspberry pi. Sound is created by engines written in <a href="/notes/supercollider">SuperCollider</a>, but rather than write Supercollider code directly, users write scripts in Lua. These scripts act as glue code between the inputs (3 encoders, 3 buttons, and any connected OSC/MIDI devices) and outputs (sound, LED screen, OSC/MIDI). You can edit the scripts using a <a href="https://monome.org/docs/norns/maiden/">web interface</a> that runs on a web server on the pi, but as it’s “just Linux” you can also SSH in, pull git repositories etc. It’s very well thought-out platform for creative coding and there’s an <a href="https://llllllll.co/">active, welcoming community</a> too.</li>
<li>My already fairly low opinion of the police was not helped by the events of the week. Heartbreaking, depressing stuff.</li>
</ul>
Weeknotes 262021-03-07T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/03/07/weeknotes<ul>
<li>We watched Armando Iannucci’s <a href="https://en.wikipedia.org/wiki/The_Personal_History_of_David_Copperfield">The Personal History of David Copperfield</a> on Thursday night. We really enjoyed it. Dev Patel is fantastic in the lead role, and other actors in the Iannucci-orbit such as Peter Capaldi, Hugh Laurie and Tilda Swinton play a host of wacky characters. Surreal, heart-warming, escapist fun.</li>
<li>I’ve been mostly doing front end work (React and CSS) for the last month on our project for the Raspberry Pi Foundation. CSS Flexbox and Grid are both new to me (almost all projects I’ve worked on where I’ve need to do frontend work in the last years have used some variation of bootstrap). <a href="https://monkeysthumb.co.uk/">Joel</a> pointed me at the excellent CSS-Tricks cheatsheets for <a href="https://css-tricks.com/snippets/css/complete-guide-grid/">grid</a> and <a href="https://css-tricks.com/snippets/css/a-guide-to-flexbox/">flexbox</a>. I’ve used React more recently so that’s not such tough going, but there’s still plenty of occassions when I don’t know how to solve a problem in React. This seems to happen quite a lot when trying to integrate some non-React library into a project (for example Chart.js in the <a href="https://github.com/luox-app">project we did for Oxford Uni</a>).</li>
<li>I recorded a piece of music everyday this week apart from Thursday. I’m mostly using Koala Sampler. I start by selecting some samples from a google drive folder and “sharing” them with Koala - this is a pretty quick way of getting them loaded in. I then create an 8 bar loop, I’ve mostly been writing lofi, house-y music at around 120BPM and I can just about tap in simple rhythms at that tempo. I use the piano editor to add extra percussion. I’ve been creating variations of the core pattern and then using the performance mode to do a “live” recording of the finished track. I only spend at most an hour on these, so they’re pretty unpolished, but it’s really interesting to listen back to them and think of ways to improve.</li>
<li>My <a href="https://monome.org/docs/norns/shield/">norns shield</a> arrived (4 days to ship from New York!). I’ve put it together (very straightforward) and had a little play. So far my favourite script has been <a href="https://github.com/schollz/mx.samples">mx.samples</a> loaded with the <a href="https://www.pianobook.co.uk/library/kawai-felt-piano/">Kawai Felt Piano</a> that I was previously using in sforzando on my laptop. It’s great to have a dedicated device that lets me play a few chords quickly.</li>
</ul>
Weeknotes 252021-02-28T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/02/28/weeknotes<ul>
<li>I had the coronavirus vaccine on Friday. I was invited by St. Thomas’s hospital (where I see the cardiology team) and attended the vaccine centre at Guy’s hospital. It was a pretty life-affirming experience to be honest, plenty of people there, well organised, cups of tea, blitz spirit etc. I felt a bit odd about being in an enclosed space (a big tent) with lots of other people (probably the largest gathering I’ve attended for over a year, ironically) but everything was being constantly sanitised. My arm was sore for a day, and I had chills/fever type symptoms for a few hours yesterday afternoon but back to normal now.</li>
<li>Afterwards I enjoyed a short walk along the Southbank in the sun.</li>
<li>When I got home and before any side effects kicked in I managed to do a bit of woodwork for the first time in months. I’m building a small cabinet on wheels for A- to store her sewing machine and overlocker in (hopefully making it easier to get started on sewing projects when she had an hour or two in the evening). Nothing special joinery wise, I’m using pocket screws to join poplar plywood. The top of the cabinet folds out to make a larger surface for the fabric being fed into the machine. I used bar-counter style flap hinges, which involved morticing into the ply. Poplar ply is pretty soft, so I was able to cut the curve with a sharp knife. I used my <a href="https://en.m.wikipedia.org/wiki/Router_plane">router plane</a> to level the bottom of the mortice.
<img src="/images/PXL_20210228_110548454-02.jpeg" alt="Counter-flap hinges in poplar ply" /></li>
<li>I’m still enjoying making music. For a bit of discipline I’ve been trying to complete whole songs even if I have very limited time, to develop a workflow and some muscle memory. I’m putting them in a folder and hopefully listening back will give me some concrete things to work on. <a href="https://www.elf-audio.com/koala/">Koala Sampler</a> is amazing, particularly with the recent update that enables piano-roll editing of sequences. Perfect for writing music while rocking F- to sleep, but it does involve a lot of staring at a screen, which I was trying to avoid. I’ve pulled the trigger on a <a href="https://monome.org/docs/norns/shield/">Norns Shield</a> which I’m hoping will be the right balance between hacking on music software and stand-alone music making. We’ll see.</li>
</ul>
Weeknotes 242021-02-21T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/02/21/weeknotes<ul>
<li>Just when I was starting to feel a bit better, I managed to put my back out. It’s taken me several days to get over that. Spending the best part of 3 weeks in doors ill isn’t great for my strength, I’m looking forward to getting out and trying to do some exercise this week.</li>
<li>A- has been watching <a href="https://www.imdb.com/title/tt5834204/">The Handmaid’s Tale</a> and I’ve been trying to avoid getting pulled into it. It’s compelling and amazingly acted but also utterly, utterly depressing. And there’s 4 series of it. I’ve had to hide at the other side of the room (we only have one room when the kids are in bed) with my headphones on to avoid it.</li>
<li>I’ve been really enjoying the new project at the Raspberry Pi Foundation. It’s a great team, and I’m enjoying being part of the creative process as well as the technical/implemention one. I wish I didn’t have to do it all from an uncomfortable desk crammed into the corner of our bedroom, but at least the hours spent there have been fun so far.</li>
<li>
<p>I picked up a <a href="https://www.gorilladj.co.uk/">Gorilla</a> flight case to hold some of my music equipment. The idea being that I can leave things connected together and it’ll be easier to start making some music once the kids have gone to bed. Bullet point 2 notwithstanding, it has worked fairly well so far. I’m really bad at cabling though, it always feels like a mess.</p>
<p><img src="/images/2021-02-21-flight-case-with-music-equipment.jpg" alt="A box of synths and other music making gear" /></p>
<p>I have a 7” <a href="https://www.waveshare.com/">Waveshare</a> screen hooked up to the Raspberry Pi (with <a href="https://blokas.io/pisound/">pisound soundcard</a>) - that’s running <a href="https://tidalcycles.org/">tidalcycles</a> in this photo. There’s a <a href="https://mackie.com/products/mix-series-compact-mixers">Makie Mix8 mixer</a>, and the <a href="https://www.korg.com/uk/products/dj/volca_keys/">Korg Volca Keys</a>. A <a href="https://www.korg.com/uk/products/computergear/nanokontrol2/">Korg nanokontrol2</a> is hidden under the massive apple keyboard.</p>
</li>
</ul>
Weeknotes 232021-02-08T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/02/08/weeknotes<ul>
<li>We’re all just about recovered from our bout of Covid. I’m still very tired a lot of the time. A few friends have told me that it took them up to a month to feel 100% again so I’m prepared for a long haul. Still, we’re luck to be mostly out of the other side of it.</li>
<li>F- started to crawl while we were all sick. Not great timing, but cute in retrospect. He’s pretty pleased with himself.</li>
<li>I started a new project today at the Raspberry Pi Foundation. We’re working with my old FutureLearn friend Laura, which is absolutely brilliant. I’m hoping I can stay well enough, and the nursery stays open long enough, to get stuck into the project.</li>
<li>Shout out to Kropotkin on the <a href="https://roarmag.org/essays/kropotkin-mutual-aid/">100th anniversary of his death</a>.</li>
</ul>
Weeknotes 222021-01-31T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/01/31/weeknotes<ul>
<li>All four of us came down with COVID this week.</li>
<li>A-, N- and I have all tested positive. We sent off a test for F- at the same time, but we haven’t got his results yet.</li>
<li>We haven’t been well. The boys have mostly had typical cough/cold symptoms. We’ve had high fevers and I’ve been having shortness of breath.</li>
<li>It’s hard looking after sick children when you’re sick yourself, and worse given the current situation. I’m thankful for my GFR colleagues and <a href="https://tuzz.tech">tuzz</a> for taking the stress of work away.</li>
<li>My medium-term ambitions have gone from “it’d be nice to have a meal out with some friends” to “it’d be nice to take the bins out without feeling exhausted”.</li>
<li>I feel like I’m on the mend though and hopefully over the worst of it. Going to take a few days to rest up as best as I can and look after the family so they can do the same.</li>
</ul>
Weeknotes 212021-01-25T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/01/25/weeknotes<ul>
<li>My inlaws returned to Colombia on Wednesday. It was very sad to see them leave, made more difficult by not quite knowing when we’ll be able to see them again. They both had a negative COVID test within 2 days of flying and I was happy at least that we’d managed to keep them safe and healthy while they were here.</li>
<li>We lost a family member in Colombia to Covid. A lovely man who had us to stay for Christmas in his house in Medellin when N- was 8 months old.</li>
<li>Having kept N- at home while A-s parents were here, on Wednesday we were able to send him back to nursery. He was excited to see his friends again and their little reunion when I dropped him off was touching. Because their access to outdoor space is a bit more limited than usual, they now have a trampolene inside. You can imagine the excitement!</li>
<li>On Thursday night we got the message that the nursery would have to close for 10 days due to 2 staff testing positive. Nursery staff are really putting themselves at risk at the moment, and Camden council have been offering regular tests. Given the prevalence of the virus at the moment, and the false positive rate for these tests I suspect short shutdowns might become more frequent. We’ve been trying to decide how best to work around this as A- is due to return to work in March.</li>
<li>It’s been a tough week, but I’m lucky to be able to spend time with my family. It snowed on Sunday and it was the first time N- had seen the snow. He loved being outside for around 10 minutes until he realised how cold it was!</li>
<li>For some light relief we’ve been enjoying the first season of <a href="https://www.imdb.com/title/tt7221388/">Cobra Kai</a>. Wonderfully silly, nostalgic fun.</li>
</ul>
Weeknotes 202021-01-18T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/01/18/weeknotes<ul>
<li>F- is <em>almost</em> starting to crawl. I really enjoy watching his physical problem solving as he works out what combination of roll, shuffle or kamikaze dive will let him grab something he shouldn’t. His favourite place to practice this is the bath, I think the added boyuncy helps a lot. And no matter how many times he slips and ends up under water - after I fish him out he goes at it again.</li>
<li><a href="https://gofreerange.com/">We</a> have agreed to start work on a new project in a few weeks time. I’m really glad. Even more than usual it feels risky to say no to certain work in order to take on something new. But taking a bit of a leap of faith has given me a lot of energy and optimism, which I need at the moment.</li>
<li>On Saturday I went on my first run since F- was born. It felt a lot better than I expected. <a href="https://www.theguardian.com/lifeandstyle/2021/jan/16/i-thrived-on-the-tension-and-drama-of-british-politics-then-i-had-a-heart-attack">Rafael Behr</a>’s harrowing account of suffering a heart attack took me back to my own encounter with St. Thomas’s cardiac unit several years ago. The paramedic who took me in thought I’d had a heart attack at the time, but after a lot of investigation it turned out to be a heart defect I didn’t know I had. Still, I don’t want to end up back there - and I know that I can miss exercise when I’m doing it regularly enough, so that seems like a good place to get back to.</li>
<li>I had some fun <a href="https://soundcloud.com/chrislowis/jamuary-16">chopping up breaks and slicing them back together</a> in one of my Jamuary tidalcycle jams. Somewhat optimistically/recklessly I see LTJ Bukem and Roni Size are <a href="https://academymusicgroup.com/o2forumkentishtown/events/1324760/roni-size-x-ltj-bukem-tickets">scheduled to play at the Forum in November</a>. Who’s with me?</li>
</ul>
Weeknotes 192021-01-10T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/01/10/weeknotes<ul>
<li>We returned to London this week. We’d accidentally turned off the
fridge/freezer before we left, so that was a messy welcome home.</li>
<li>I’d planned to work with <a href="https://jamesmead.org/">James</a> on our
project for Oxford University. We spent a bit of time
remotely-together but I mostly ended up having business development
calls, a workshop with <a href="https://space4.tech/workspace/">Space4</a> and
making an application for a potential project. And also staring
slack-jawed at the TV while some fash broke/were let into the Capitol.</li>
<li>N- and I went for a short walk, and spent quite a bit of time
watching two squirrels build a nest (or a
<a href="https://www.woodlandtrust.org.uk/blog/2020/06/squirrel-nests/">drey</a>,
apparently).</li>
<li><a href="https://samstarling.co.uk/weeknotes/">Sam</a> and
<a href="https://www.eightbitraptor.com/2021/01/04/weeknotes-2021.00-late-to-the-party/">Matt</a>
started writing weeknotes! Sam even <a href="https://twitter.com/samstarling/status/1348355475990081536">added an RSS
feed</a>
just for old, old me, thanks Sam.</li>
<li>I’ve been jamming (not <a href="https://twitter.com/hlame/status/1346183098787098624">the delicious
kind</a>, sadly)
fairly regularly. Mostly with tidalcycles, and in particular with
the <a href="https://github.com/v7b1/mi-UGens">Mutable Instruments Supercollider
ports</a>. I wrote a bit about one
jam and shared some music in the <a href="https://club.tidalcycles.org/t/mutable-instruments-ugens/2730">tidalcycles
club</a>.</li>
</ul>
Weeknotes 182021-01-04T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2021/01/04/weeknotes<ul>
<li>Happy New Year.</li>
<li>We’ve spent another week holed up in our rental house in
Oxford. It’s good to have the extra space but I think N- is getting
ready for a change of scene. The local playground is flooded and
that’s about the extent of accessible, safe outdoor activities.</li>
<li>On Saturday I went for a walk with my friend Aaron from fellow co-op
<a href="https://agile.coop/">Agile Collective</a>. It was good to catch up and
try and be a bit hopeful for 2021.</li>
<li>I got a burst of enthusiasm to write last week so I decided to put
together <a href="https://www.webaudioweekly.com/108">another issue of my
newsletter</a>. It was
heart-warming to receive a few emails from readers thanking me and
saying that they’d missed it. I’m going to try to keep a regular
schedule for these again for a while.</li>
<li>I’ve managed to make a small amount of music each day for
<a href="https://www.jonathanmann.net/jamuary">Jamuary</a>. At the moment
figuring out how to record and share them would mean I don’t bother
making them, but I’m hoping to upload one or two this week when I
get back home. I’m still enjoying learning the ins and outs of
<a href="https://tidalcycles.org/Welcome">TidalCycles</a>, mostly by
translating sounds and techniques that I see others using on
YouTube to the Tidal environment.</li>
<li>Arsenal are playing fairly well at the moment, which cheers me up.</li>
<li><a href="https://po-ru.com/2021/01/03/week-0">Paul</a> started writing
weeknotes which has also cheered me up!</li>
<li>
<p>After my hair was described by A- as looking “a bit Doc Brown” it
was time for a lockdown haircut. We borrowed my father-in-law’s
beard trimmer (he has a very stylish beard) and A- hacked everything
off. It’s now a much more respectable 3mm long. The kids thought
this was hilarious. Rather than share a picture of my ugly mug,
here’s N-‘s “before and after” rendition:</p>
<p><img src="/images/2021-01-04-daddy.jpg" alt="Daddy" /></p>
</li>
</ul>
Weeknotes 172020-12-29T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/12/29/weeknotes<ul>
<li>These weeknotes are a bit late, but given I have no idea what day of
the week it is, I think I can style it out.</li>
<li>F- has started to say a couple of words including, gratifyingly,
“dada”. He’s also finding the word “agua” (water) extremely useful
when thirsty, but also as a stand in for “milk” and “bath”.</li>
<li>I’m really missing my family this Christmas.</li>
<li>N- is old enough to play games now. We’ve had fun playing Jenga,
memory games and a one he invented called “Lego Skittles”. He’s
quite competitve and always wants to be “the winnest”. This is
proving quite handy when we want him to do something quickly - “who
can put their shoes on the fastest N-, you or daddy?”.</li>
<li>I miss Chris Z’s weeknotes, but I see he’s recently started to
publish an <a href="https://chriszetter.com/blog/2020/12/27/my-cookbook/">annotated collection of
recipes</a> which
is a great idea.</li>
<li>
<p>I enjoyed Allison Parrish’s talk <a href="http://opentranscripts.org/transcript/programming-forgetting-new-hacker-ethic/">Programming is Forgetting: Toward
a New Hacker
Ethic</a>
which takes a critical look at Steven Levy’s book <em>Hackers: Heroes
of the Computer Revolution</em> and revisits the “hacker ethic” a way
that is more aware of structural inequalities</p>
<blockquote>
<p>Instead of saying access to computers should be unlimited and total,
we should ask “Who gets to use what I make? Who am I leaving out?
How does what I make facilitate or hinder access?”</p>
</blockquote>
</li>
<li>Happy New Year. I very much hope you and your loved ones are well,
and we can go for a coffee or a pint before too long.</li>
</ul>
Weeknotes 162020-12-21T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/12/21/weeknotes<ul>
<li>My in-laws arrived and after a period of self-isolation and a negative COVID test, they finally got to meet F- for the first time.</li>
<li>🥲</li>
<li>I finished up at work for the year. We managed to complete our piece of the project on time and along the way advocated for some more agile ways of working which I think helped the team as a whole.</li>
<li>I also finished my Barefoot Co-op Development training course. I’ve been meeting my 20 or so class mates via zoom every other Tuesday for the last 6 months and it was sad to say goodbye. I’m hoping we’ll all be able to get together as planned at some point next year. I now know a bit more about how to start and run a co-operative business and I’m thinking about how best to use this knowledge in 2021.</li>
<li>I made my first submission to the <a href="https://llllllll.co/t/disquiet-junto-project-0467-toolbox-show-tell/39254/32">Disquiet Junto weekly composition challenge</a>. It’s a very simple piano piece. I have very little time to make music but I enjoyed having a challenge and a self-imposed deadline (about an hour).</li>
<li>I’m tired and lonely and I wish I could be there for my family and friends who are having a harder year than I am.</li>
</ul>
<h1 id="listening">Listening</h1>
<ul>
<li><a href="https://dauw.bandcamp.com/album/companion">Companion</a> by Jogging House.</li>
</ul>
Weeknotes 152020-12-06T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/12/06/weeknotes<ul>
<li>My in-laws arrived from Colombia on Friday. One of the hardest
things about this year has been the intense feeling of separation
from them. They haven’t met F- yet (who was born in April) and we’ve
known that if we needed to get to them at that would have been
almost impossible (<a href="/notes/bogota.html">Bogota</a> airport has been closed until very
recently). Although they have to self-isolate now they have arrived,
we have the joy of seeing them meet F- for the first time to look
forward to.</li>
<li>I caught up with some friends this week. By video call on Wednesday
night, over slack conversations during the week, and even in
person(!) on Friday during a long walk over the Heath.</li>
<li>After catching up on admin tasks on Friday I spent some time
thinking about if and how we might be able to grow <a href="https://gofreerange.com">Go Free
Range</a> next year. For a long time we’ve had
a chicken-and-egg problem. Either we don’t have enough work to think
about taking someone else on, or we have too much work and we don’t
have enough time to do it. We’re thinking creatively about how to
solve this, and I’m looking forward to chatting about it when we
meet next week.</li>
<li>I got <a href="https://tidalcycles.org/">TidalCycles</a> running on my
Raspberry Pi this week. Someone has <a href="https://github.com/cleary/ansible-tidalcycles-base/tree/master/tasks">created an ansible
role</a>
which helped me figure out some of the dependencies. I also made a
<a href="https://github.com/chrislo/dotemacs/commit/f86a34d06f5da883f56265841b7daea9e2215a27">couple of small
tweaks</a>
to my emacs config so that would also run on the pi. I’m now able to
use my phone as a terminal connected to the pi, and sequence MIDI
events to my <a href="https://www.korg.com/uk/products/dj/volca_keys/">Volca
Keys</a>. The keys
understands MIDI Control Change messages for many of its parameters,
and I’ve had fun sequencing the filter cutoff while playing chords.</li>
</ul>
<p><img src="/images/tidal_pi_volca.jpg" alt="Raspberry Pi, Volca Keys" /></p>
<h2 id="listening">Listening</h2>
<p>It was the last Bandcamp Friday (where Bandcamp passes on all of the
purchase price to the artist). I picked up:</p>
<ul>
<li><a href="https://rbeny.bandcamp.com/album/seafoam-dust">Seamfoam & Dust by r
beny</a> (ambient,
synth, tape noise)</li>
<li><a href="https://shallnotfade.co.uk/album/cathedrals-ep">Cathedrals EP by Harrison
BDP</a> (lofi
house/techno)</li>
</ul>
<p>I’ve been enjoying the new Bahamas record <a href="https://www.allmusic.com/album/sad-hunk-mw0003421302">Sad
Hunk</a>. This led
me back to a wonderful live version of “Lost in the light” which is
now on repeat:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/0Tw17XPs1Dg" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
Weeknotes 142020-11-29T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/11/29/weeknotes<ul>
<li>Another quiet week. I’m busy with work at FutureLearn, it feels good
to be writing and mostly shipping useful code. I’m really enjoying
having a couple of days a week pairing with
<a href="https://tuzz.tech/">Chris</a> and it’s also been great to work with
<a href="https://www.urbanautomaton.com/">Simon</a> again.</li>
<li>I found out this week that large parts of the <a href="https://www.bbc.co.uk/programmes/articles/BHmrZF1Z7JDckzFSZ9cl7l/changes-to-the-bbc-music-website">BBC
/music</a>
site have been taken offline. Things have moved on, and <a href="https://www.bbc.co.uk/sounds">BBC
Sounds</a> seems to be focus today. I
moved to London to <a href="/2009/08/25/recent-activities.html">join the BBC in
2009</a> to work on <code class="language-plaintext highlighter-rouge">/music</code>. One
of the wonderful things about <code class="language-plaintext highlighter-rouge">/music</code> was its commitment to have <a href="https://www.bbc.co.uk/blogs/bbcinternet/2008/07/bbc_music_artist_pages_beta.html">a
page for every
artist</a>
which collected all of their BBC connections (songs played on the
radio, interviews, album reviews etc.) in one place. Rather than
build a CMS to edit the artist biographies, we instead pulled
changes from Wikipedia and Musicbrainz. This meant our editorial
colleagues (we sat right next to them, which was another great thing
about the team) were editing Wikipedia in order to edit <code class="language-plaintext highlighter-rouge">/music</code>,
improving both at the same
time. <a href="https://twitter.com/metade">Patrick</a> and others had written
some scripts that used a Wikipedia IRC channel to get a stream of
change events, filtered them for pages we cared about and then
updated the artist biographies. It was elegant and janky and
brilliant and I loved it. I mostly worked on <code class="language-plaintext highlighter-rouge">/introducing</code>, the
BBC’s service for unsigned artists. We looked after a Rails app
called <code class="language-plaintext highlighter-rouge">Peel</code>, and one of its jobs was to create a new page on
<code class="language-plaintext highlighter-rouge">/music</code> when one of the introducing artists had been played on the
radio for the first time. It was a small thing, but we got such
great feedback from artists for that. Anyway, nostalgia isn’t what
it used to be and <a href="https://www.w3.org/Provider/Style/URI.html">URLs aren’t cool
anymore</a>. But I’m glad
for the memories and the friendships I still have from that time.</li>
<li>We went shopping for a Christmas tree and decorated it today. N- is
now at an age where this is very exciting for him, and given the
year we’ve all had, I’m going to get stuck in. The
<a href="https://www.bbc.co.uk/music/artists/42faad37-8aaa-42e4-a300-5a7dae79ed24">Low</a>
<a href="https://musicbrainz.org/release-group/031c1507-7402-3727-a102-f01403f52df3">Christmas
Album</a>
made its first (of many) appearances.</li>
<li>I’ve given up on <a href="https://github.com/hlissner/doom-emacs">doom
emacs</a>. There were too many
things that were slightly different to how I was used to them
working, and I didn’t have enough time to learn them (or even to
learn how to disable them). The final straw was when I had issues
with some trailing whitespace not deleting reliably, and couldn’t
work out how to fix it. It’s not been a lost cause though, I’ve
found a few new packages that are great (<code class="language-plaintext highlighter-rouge">vterm</code> in particular) and
know enough about doom to crib things from its code into my own
config. I’ve <a href="https://github.com/chrislo/dotemacs">started a new
config</a> from scratch and am
pulling things from my old config, or from doom, as and when I need
them. So far, so homely.</li>
</ul>
<h2 id="listening">Listening</h2>
<ul>
<li><a href="https://blankforms.bandcamp.com/album/memory">Memory by BlankFor.ms</a></li>
<li><a href="https://jackdalemusic.bandcamp.com/album/youngest-son">Youngest Son by Jack Dale</a></li>
</ul>
Weeknotes 132020-11-23T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/11/23/weeknotes<ul>
<li>I started working at <a href="https://www.futurelearn.com/">FutureLearn</a> again this
week. They’ve asked <a href="https://gofreerange.com/">us</a> to help with a couple of
projects and it’s good to be back. It goes without saying that the working
environment is dramatically different to the last time I was there - everyone
is now fully remote. <a href="https://tuzz.tech/">Chris</a> has joined me to help out
too, and it’s great to finally get chance to work with him.</li>
<li>Life is otherwise pretty quiet, there’s not a lot to do in lockdown London.
We’ve been lucky to have the Heath and some reasonable weather. N- enjoyed a
trip to the playground and the <a href="http://parliamenthilllido.org/cafe">lido cafe</a>
makes something he’ll eat (and has a tasty falafel stand outside on weekends)
so we had a wintery picnic.</li>
<li>With no travel to pay for I’ve been able to save a little bit in the last few
months. I treated myself to a <a href="https://koma-elektronik.com/?product=field-kit">Koma Field
Kit</a> to use as a small mixer
and because <a href="https://audionewsroom.net/2017/06/finding-beauty-in-imperfections-qa-with-hainbach-about-his-koma-field-kit-setup.html">Hainbach has one</a>. I’ve been able to connect my <a href="https://www.korg.com/uk/products/dj/volca_keys/">Volca
Keys</a> and <a href="https://teenage.engineering/guides/po-33/en">PO-33
sampler</a> to the built in mixer
and mash that up with some radio static and buzzy noises from the LFO. The
setup just puts a huge smile on my face. Not to sound like too much of an old
man, but as a teenage music technology student I would have been blown away by
the capabilities of these little instruments.</li>
<li>I had a couple of Zoom calls with friends. That tradition seemed to die away a
bit over the summer, but I really enjoyed catching up. One of my friends has
lived in Singapore for years so we scheduled a chat for Friday lunchtime UK
time. That worked pretty well (although I was a bit jealous of his beer).</li>
<li>I’m down from around 93kg at the start of the year to just over 85kg now. I’d
like random online calculators to stop calling me “overweight”, which I think
means a BMI of 25 or less (< 81kg). A fairly plain diet and not too many beers isn’t
a lot of fun during lockdown but it’s also not a bad time to do it. I’m
pleased with my progress so far.</li>
</ul>
Weeknotes 122020-11-15T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/11/15/weeknotes<ul>
<li>No weeknotes last week. My 7-month old baby faced some stiff competition in
the “keeping Chris awake at night game” from bitter rival “the absolute state
of the world”. The former won. In your face world!</li>
<li>I’ve been working on my own on two projects for Oxford University this week.
After <a href="https://mudge.name/2020/11/08/weeknotes-54/">pairing with Paul</a> last
week it’s been good to force myself to consolidate what I’ve learnt about
React by adding some features on my own.</li>
<li>One of the many things I learnt while working with Paul was the <a href="https://devcenter.heroku.com/articles/heroku-local">heroku
local</a> command. A handy
shortcut for the <code class="language-plaintext highlighter-rouge">dotenv</code> and <code class="language-plaintext highlighter-rouge">Procfile</code> fiddling I was doing before.</li>
<li>N- decided he wanted to try wearing pants again on the same day our washing
machine packed up. We tried to get it fixed (by the excellently-named and
highly recommended <a href="https://www.spindoctoruk.co.uk/">Spin Doctor</a>) but
unfortunately it’s a gonner. Our new machine arrived yesterday and it connects
to the internet. WHY?!</li>
<li>
<p>I found a bit more time to play with <a href="https://tidalcycles.org/">TidalCycles</a>
this week. First I wanted to make my drums bounce a little by adding some
<a href="https://en.wikipedia.org/wiki/Ghost_note">ghost notes</a> - quieter hits played
before or after a main hit. I came up with</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> d1 $ superimpose (sometimes ((1/16) <~) . (# gain 0.5))
$ sound "[kick ~ kick ~]"
</code></pre></div> </div>
<p>this <a href="https://tidalcycles.org/index.php/sometimes">sometimes</a> plays a lower
volume kick drum a 16th before the hit on the 1 or 3. Using
<a href="https://tidalcycles.org/index.php/superimpose">superimpose</a> means I can
specify the pattern in terms of the “main” rhythm and let the function take
care of creating the pre-delayed copy.</p>
<p>In the tidalcycles tutorial that runs on <a href="https://club.tidalcycles.org/">Tidal Club</a> I also learnt about <a href="https://tidalcycles.org/index.php/chop">chop</a> for slicing and rearranging samples. Putting the two techniques together I made this loop:</p>
<audio controls="">
<source src="/sound/bounce_and_chop.ogg" type="audio/ogg" />
Your browser does not support the audio tag.
</audio>
<p>I’m enjoying Tidal. I’m not sure I’ll get into the whole
<a href="https://en.wikipedia.org/wiki/Algorave">algorave</a> thing, but I can quickly
make patterns that would take a lot of clicking and copy-pasting in a DAW. I’d
still like to get some external MIDI control hooked up so that I can spend
less time looking at the screen and more time “playing”.</p>
</li>
</ul>
<h2 id="reading">Reading</h2>
<ul>
<li><a href="https://blog.basilesimon.fr/">Basile’s weeknotes</a> on data journalism,
freelancing and parenting.</li>
<li><a href="https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/">Parse don’t validate</a> (via <a href="https://twitter.com/beng/status/1324344967561621504">Ben</a>)</li>
</ul>
<h2 id="listening">Listening</h2>
<ul>
<li><a href="https://www.washedupemo.com/">Washed Up Emo Podcast</a> (via Dan)</li>
</ul>
Weeknotes 112020-11-01T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/11/01/weeknotes<ul>
<li>The halloween celebrations lasted all week at N-‘s nursery. We usually do
something for halloween, because it’s also A-‘s birthday, so growing up it was
always a big deal for her. Now with kids it’s also an excuse to have some
silly fun. N- had his face painted most days this week. One attempt at
painting a dinosaur didn’t last beyond a teary tantrum before we left the
house. But the Spiderman and Hawaiian-shirt combo on Friday was a huge
success!</li>
<li>I cooked a few new things this week:
<ul>
<li>A tofu and noodle bowl dish from <a href="http://www.annajones.co.uk/books/modern-way-eat">Anna Jones’ A modern way to eat</a> (h/t <a href="https://tomstu.art/weeknotes-0-collectively-meaningless">Tom</a>). I used the Tofoo brand <a href="https://tofoo.co.uk/products/smoked/">smoked tofu</a> which was delicious. F- enjoyed eating a small piece too. Aw, babies.</li>
<li>A stir fry from the same book - the sauce (which includes maple syrup) will be very useful for lots of quick meals I think.</li>
<li>I improvised a sausage, bean and red cabbage casserole. I often make something like this but this time I added a good glass of red wine which made a big difference.</li>
<li>For A-‘s birthday I tried to help her homesickness by cooking some Colombian
dishes. I made some empanadas based on <a href="https://www.jamieoliver.com/recipes/beef-recipes/beef-empanadas/">this Jamie Oliver
recipe</a>. I
used some pre-made filo pastry squares I picked up from
<a href="https://www.phoeniciafoodhall.co.uk/">Phoenicia</a>. They were the highlight. I tried to make <a href="https://en.wikipedia.org/wiki/Ajiaco">Ajiaco</a>, but I couldn’t find guascas or any of the typical potatoes, so it was just a chicken and sweetcorn soup. It was ok. Finally I attempted <a href="https://www.youtube.com/watch?v=llmP3KKdKdY&feature=emb_logo">a HidaMari no-bake cheesecake</a> but using some Colombian mora fruit puree. It was a bit of a sloppy disaster, but again, tasted ok.</li>
</ul>
</li>
<li>
<p>I continued to play around with Tidalcycles. I got it <a href="https://tidalcycles.org/index.php/SuperDirt_MIDI_Tutorial">talking to my Volca Keys via MIDI</a> fairly easily, although I haven’t quite figured out how to send clock messages that the Volca understands. The keys doesn’t have a built-in arpeggiator, so I had a lot of fun creating arpeggios in tidal while sweeping the filters on the keys by hand. The keys is polyphonic, but the little keyboard makes it hard to play bass and lead lines at the same time. This is easy to achieve in tidal.</p>
<p>I also played around with “humanising” drums using <code class="language-plaintext highlighter-rouge">swingBy</code> and <code class="language-plaintext highlighter-rouge">gain</code>
patterns. I made this simple boom bap style beat</p>
<audio controls="">
<source src="/sound/boombap.ogg" type="audio/ogg" />
Your browser does not support the audio tag.
</audio>
<p>using the following code</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>setcps (90/60/4)
d1 $ swingBy (1/32) 4
$ sound "closedhat(8,8)"
# gain "[0.7 0.5]*4"
d2 $ sound "~ snare ~ snare"
d3 $ sound "[kick ~ kick kick ~ ~ ~ kick kick ~ ~ ~ ~ ~ ~ kick]"
# gain "[1 0 0.7 0.6 0 0 0 0.7 1 0 0 0 0 0 0 0.7]"
</code></pre></div> </div>
<p>The code for the kick pattern in particular is pretty literal so I’m going to work out if there’s a more idiomatic way of writing it. I’d also like to play with creating variations of the pattern.</p>
</li>
<li>It’s looking like we’ll be spending a lot of time outdoors with the kids this
winter. N- is still enjoying climbing trees and exploring Hampstead Heath
which is a good sign. I need some new boots and waterproof clothes.</li>
</ul>
Weeknotes 102020-10-26T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/10/26/weeknotes<ul>
<li>On Sunday morning both boys were up stupidly early (thanks daylight saving
time), but last night was the first night F- has slept through without waking,
so go figure.</li>
<li>We went on the <a href="https://www.kew.org/kew-gardens/whats-on/gruffalo-october-half-term">Gruffalo
Adventure</a>
at Kew Gardens on Saturday. Kew has generously extended our membership for a
couple of months to make up for the time they were closed. N- enjoyed hunting
for Gruffalo characters and filling in a nature-themed treasure hunt. As long
as we can keep moving around the city, I think Kew might be a popular
destination for us this winter, there’s plenty to do outdoors and the large
glasshouses are warm and colourful.</li>
<li>I met Joel for a catch up on Monday night, it was really great to chat for a
couple of hours.</li>
<li>
<p>I’ve been playing with
<a href="https://tidalcycles.org/index.php/Welcome">TidalCycles</a> a bit this week. It’s
a lot of fun, and I’ve just started to scratch the surface. I hadn’t realised
that it has a <a href="/notes/supercollider">SuperCollider</a> audio engine,
so some of the things I’ve been learning this year should transfer over. I
still don’t really want to sit in front of a keyboard and screen to make
music, but I like the idea of being able to create little audio “applications”
in Tidal that control external equipment and can be controlled by MIDI
controllers.</p>
<p>I enjoy the <a href="https://www.attackmagazine.com/">Attack Magazine</a> series <a href="https://www.attackmagazine.com/technique/beat-dissected/">beat
dissected</a>. It shows
how to create music in a variety of different styles, and also includes samples. Using Tidal I made a version of the <a href="https://www.attackmagazine.com/technique/beat-dissected/lo-fi-house/">lo-fi house</a> beat:</p>
<audio controls="">
<source src="/sound/tidal_house.ogg" type="audio/ogg" />
Your browser does not support the audio tag.
</audio>
<p>Tidal code is Haskell, which I’m not familiar with at all. But the Tidal
language embedded in it seems to be usable even without any knowledge. The
above snippet was created with this code:</p>
<div class="language-haskell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">setcps</span> <span class="p">(</span><span class="mi">125</span><span class="o">/</span><span class="mi">60</span><span class="o">/</span><span class="mi">4</span><span class="p">)</span>
<span class="n">d1</span>
<span class="o">$</span> <span class="n">slow</span> <span class="mi">2</span>
<span class="o">$</span> <span class="n">sound</span> <span class="s">"lofi:2 lofi:2 lofi:2 lofi:2 lofi:2 [lofi:2 lofi:2 ~ ~] lofi:2 [lofi:2 ~ ~ lofi:2]"</span>
<span class="o">#</span> <span class="n">hpf</span> <span class="mi">30</span>
<span class="o">#</span> <span class="n">note</span> <span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="o">#</span> <span class="n">release</span> <span class="p">(</span><span class="mf">0.4</span><span class="p">)</span>
<span class="o">#</span> <span class="n">shape</span> <span class="mf">0.4</span>
<span class="n">d2</span> <span class="o">$</span> <span class="p">(</span><span class="mf">0.01</span> <span class="o"><~</span><span class="p">)</span> <span class="o">$</span> <span class="n">sound</span> <span class="s">"~ lofi:1 ~ lofi:1"</span> <span class="o">#</span> <span class="n">note</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">d3</span> <span class="o">$</span> <span class="p">(</span><span class="mf">0.01</span> <span class="o"><~</span><span class="p">)</span> <span class="o">$</span> <span class="n">sound</span> <span class="s">"~ lofi:3 ~ ~"</span> <span class="o">#</span> <span class="n">release</span> <span class="mf">0.2</span>
<span class="n">d4</span> <span class="o">$</span> <span class="n">randslice</span> <span class="mi">4</span> <span class="o">$</span> <span class="n">sound</span> <span class="s">"lofi:4"</span> <span class="o">#</span> <span class="n">hpf</span> <span class="mi">150</span> <span class="o">#</span> <span class="n">gain</span> <span class="mf">0.7</span>
<span class="n">d5</span> <span class="o">$</span> <span class="n">swingBy</span> <span class="p">(</span><span class="mf">0.35</span><span class="p">)</span> <span class="mi">4</span> <span class="o">$</span> <span class="s">"~ lofi ~ lofi"</span> <span class="o">#</span> <span class="n">release</span> <span class="mf">0.1</span> <span class="o">#</span> <span class="n">lpf</span> <span class="mi">10000</span>
<span class="n">d6</span> <span class="o">$</span> <span class="n">swingBy</span> <span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="mi">4</span> <span class="o">$</span> <span class="n">sound</span> <span class="s">"lofi:5(5,8,3)"</span> <span class="o">#</span> <span class="n">release</span> <span class="mf">0.15</span> <span class="o">#</span> <span class="n">lpf</span> <span class="mi">5000</span> <span class="o">#</span> <span class="n">gain</span> <span class="mf">0.7</span>
<span class="n">d7</span> <span class="o">$</span> <span class="n">slow</span> <span class="mi">4</span> <span class="o">$</span> <span class="n">n</span> <span class="s">"f4'maj7 c4'maj7"</span> <span class="o">#</span> <span class="n">s</span> <span class="s">"superfm"</span> <span class="o">#</span> <span class="n">gain</span> <span class="mf">0.4</span> <span class="o">#</span> <span class="n">attack</span> <span class="mf">0.5</span> <span class="o">#</span> <span class="n">release</span> <span class="mi">2</span> <span class="o">#</span> <span class="n">lpf</span> <span class="mi">200</span>
</code></pre></div> </div>
<p><code class="language-plaintext highlighter-rouge">d1</code> to <code class="language-plaintext highlighter-rouge">d7</code> are equivalent to “tracks”. I’ve loaded the samples from the
Attack magazine article into the unhelpfully-named <code class="language-plaintext highlighter-rouge">lofi:n</code> variables.
<code class="language-plaintext highlighter-rouge">lofi:2</code> is the kick drum, for example.</p>
<p><a href="/notes/supercollider">SuperCollider</a> has a built in “record to file” option and I imported the <code class="language-plaintext highlighter-rouge">AIFF</code>
created by that into Audacity to add a fade-in and out and covert to <code class="language-plaintext highlighter-rouge">OGG</code>.</p>
<p>I think I have everything I need to get this running on the raspberry pi and
I’d like to explore using Tidal to output MIDI next.</p>
</li>
</ul>
Weeknotes 92020-10-18T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/10/18/weeknotes<ul>
<li>On reflection, <a href="https://blog.chrislowis.co.uk/2020/10/11/weeknotes.html">opening a weeknotes</a> with “several years ago” is a bit of a cop-out.</li>
<li>I was out-and-about a bit this week. I met an old colleague on Tuesday afternoon, my GFR comrades on Wednesday and a friend on Friday night. The latter was the first time I’ve been out of the house after 7pm for several months. The beer garden felt safe, table service was a bit of a novelty and the 10pm curfew seemed dangerously arbitrary. But with a pint in hand I probably would say that.</li>
<li>These meetings all involved sitting outside and as the weather gets colder I need some more layers. Vest or <a href="https://www.tacticaldistributors.com/collections/tactical-base-layers">tactical base-layer</a>?</li>
<li>I had another session of the <a href="https://www.culture.coop/barefoot-training/">Barefoot Co-op Development</a> training course on Tuesday. This time the topic was conflict resolution. In groups, we helped an imaginary co-op pub to handle some conflict between their staff. I found the (Zoom) group work quite tiring but very rewarding. In our group we had two members of an <a href="https://foxandgoose.org/">actual co-op community pub</a> and I learnt a lot from discussing everyone’s real experiences.</li>
<li><a href="https://space4.tech/">Space4</a> have asked me to help run a workshop on legal structures as part of their plan to become a co-op in their own right. I’m excited but also nervous about putting some of what I’m learning into practice.</li>
<li>I received another couple of emails this week asking what happened to <a href="https://webaudioweekly.com">web audio weekly</a>. I’ll try and get an issue out this week, but if you, dear reader, ever consider starting a newsletter my advice to you is to avoid using the millstone word “weekly” in its title.</li>
<li>So ends these weeknotes. Oh.</li>
</ul>
Weeknotes 82020-10-11T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/10/11/weeknotes<ul>
<li>
<p>Several years ago I felt faint on the way to work, so decided to head to my GP
instead. Shortly after I was admitted to St. Thomas’s cardiac ward. After a
couple of days of investigation they discovered I had a thickening of the wall
of my heart. Although this causes a slight obstruction, with medication I no
longer have any symptoms. Although we don’t know the cause of this thickening
one of the possibilities <a href="https://en.wikipedia.org/wiki/Hypertrophic_cardiomyopathy">could be
inheritable</a> so
when F- was born they referred him to Great Ormond Street. We had our (video)
consultation this week, and they’ve asked to see N- too when it’s safe to
attend in person. It’s rare for there to be any complications, but I’m happy
that they want to keep on eye on the boys as they get older.</p>
</li>
<li>
<p>In other Zoom call news, we had a fun 10-way call with our Colombian family to
celebrate the marriage of A-‘s cousin. We had family dialing in from two
cities in Colombia, Australia, California, Japan and the UK. Later in the week
we also managed to catch my father-in-law in a WhatsApp video call while he
celebrated his birthday. It was odd to see the familar COVID-trappings (face
masks, painted lines 2m apart on the floor) in the restaurant where he was
having lunch.</p>
</li>
<li>
<p>I love Ethan Hein’s hip-hop education blog. His <a href="http://www.ethanhein.com/wp/2020/what-is-going-on-in-this-noname-beat/">recent post on septuplet
swing</a>
introduced me to this wonderful track from Noname</p>
<iframe style="border: 0; width: 100%; height: 120px;" src="https://bandcamp.com/EmbeddedPlayer/album=1599515291/size=large/bgcol=ffffff/linkcol=0687f5/tracklist=false/artwork=small/track=2653206599/transparent=true/" seamless=""><a href="https://nonameraps.bandcamp.com/album/telefone">Telefone by Noname</a></iframe>
<p>I’ve played the whole album through at least half a dozen times this week, it’s great.</p>
</li>
<li>
<p>I found some time to get my
<a href="https://github.com/chrislo/brain">sequencer/sampler</a> project running on a
Raspberry Pi. I’d attempted this earlier in the year and the MIDI-to-audio
latency made it quite unplayable. With the <a href="https://blokas.io/patchbox-os/">Patchbox
OS</a> distribution the latency is much better (I
haven’t measured it yet, but it is just barely detectable to me now). I only
have a cheap USB sound card, and I think the
<a href="https://blokas.io/pisound/">pisound</a> hat is likely to improve things a lot.
I’m quite tempted.</p>
</li>
<li>
<p>N- and I have been talking about autumn and different types of tree on our way
to and from nursery this week. Today we all took a walk on Hampstead Heath and
he enjoyed climbing trees and talking about the colours of their leaves. We
got confused by some larger acorns on what looked like a small oak tree.
Google Lens identified it as a <a href="https://en.wikipedia.org/wiki/Quercus_rubra">Northern Red
Oak</a> from a photo of its leaves.
What a world, etc.</p>
</li>
</ul>
Weeknotes 72020-10-03T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/10/03/weeknotes<ul>
<li>
<p>I’ve continued to work from the Outlandish office in Finsbury Park. It’s still
very quiet with at most 4 other people on a busy day. Having a comfortable set
up, fast internet connection and a quiet environment makes a huge difference.</p>
</li>
<li>
<p><a href="https://www.ghostcassette.com/">Paul</a> offered to work with me on a project we
have with Oxford University. I’ve really enjoyed (remote) pairing and learned
a huge amount about React. It’s made me want to find opportunities to work
with other people more often (and Paul again).</p>
</li>
<li>
<p>F- turned 6 months this week. Things are getting slightly easier now N- is
able to go to nursery. A couple of weeks ago we started helping F- to sleep on
his own (a euphemistic way of saying “sitting outside his door crying while he
cries”) following the method in <a href="https://www.ethicalbooksearch.com/uk/books/m/ol:OL19992608W/the-happy-sleeper-heather-turgeon">The Happy Sleeper</a>. It had got to the stage
where neither A- nor I were getting much sleep at all, and F- was noticeably
angry at our usual attempts to rock him to sleep. Within a few nights he’d
learnt to fall asleep quietly on his own.</p>
</li>
<li>
<p>F- has also had a snotty cold. It’s a bit odd seeing him so grumpy as he’s
usually of a very sunny disposition. Case in point, here he is in an unusual
situation, with bright lights shining in his face after being given the
explicit instruction not to smile:</p>
</li>
</ul>
<p><img src="/images/2020-10-03-smiling-f.jpg" alt="Smile" /></p>
<ul>
<li>
<p>I’m still getting a lot of enjoyment from the <a href="http://kellyleeowens.bandcamp.com/album/inner-song">Kelly Lee
Owens</a> album I mentioned
<a href="/2020/09/06/weeknotes.html">a few
weeks ago</a>. Her song “On” was recently <a href="https://songexploder.net/kelly-lee-owens">featured on the song exploder podcast</a>. I enjoyed her talking about leaving in some “mistakes” generated by her analogue modular synth and how those mistakes tie into the broader themes of the song.</p>
</li>
<li>
<p>I opened a news app this week to be confronted with a face I recognised. The
spokesman for a far-right German political party was caught on mic talking
like a Nazi. For some reason this surprised the party and they’ve sacked him.
He was a class mate of my housemate when I lived in Berlin in the early 2000s
and we’d spent quite a lot of time together. At the time he was active in the
liberal party, but the last 20 years have clearly radicalised people both here
and in Germany. My old housemate and I comforted ourselves via WhatsApp. We’d
always thought he was an <em>Arsch</em> anyway.</p>
</li>
</ul>
Weeknotes 62020-09-21T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/09/21/weeknotes<ul>
<li>We have a new bathroom!</li>
<li>Which means we’ve been able to move back home. I’ve really missed being able to find a sharp kitchen knife.</li>
<li>On Tuesday I decided to stop procrastinating and write an application for a really interesting-sounding opportunity on a popular government open tender site. A lot of companies that go for this kind of thing have people who write the applications, and a separate set of people that end up doing the work. At <a href="https://gofreerange.com">freerange</a> we’re the same people. This has a bunch of downsides, but one of the main upsides (for our clients at least) is that when we apply for a project, we really want to do it. I was pleased with the application I put together, it took a couple of hours (plus some extra hours of faffing close to the deadline while I shuffled words around aimlessly). I’m fully anticipating having our hopes dashed.</li>
<li>Harry and Abi from Outlandish came by the office on Thursday and we went to the pub for a drink before heading home. That’s the first time I’ve sat in (outside) a pub with some friends for over 6 months. It might be the last time for a while too.</li>
<li>I really enjoyed this interview with Wendy Liu (Abolish Silicon Valley) on the Upstream Podcast</li>
</ul>
<iframe width="100%" height="300" scrolling="no" frameborder="no" allow="autoplay" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/895687342&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true"></iframe>
<div style="font-size: 10px; color: #cccccc;line-break: anywhere;word-break: normal;overflow: hidden;white-space: nowrap;text-overflow: ellipsis; font-family: Interstate,Lucida Grande,Lucida Sans Unicode,Lucida Sans,Garuda,Verdana,Tahoma,sans-serif;font-weight: 100;"><a href="https://soundcloud.com/upstreampodcast" title="Upstream" target="_blank" style="color: #cccccc; text-decoration: none;">Upstream</a> · <a href="https://soundcloud.com/upstreampodcast/wendy-liu-on-aboloshing-silicon-valley-in-conversation" title="Wendy Liu on Aboloshing Silicon Valley (In Conversation)" target="_blank" style="color: #cccccc; text-decoration: none;">Wendy Liu on Aboloshing Silicon Valley (In Conversation)</a></div>
Weeknotes 52020-09-13T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/09/13/weeknotes<ul>
<li>Avid readers of my domestic enumeration will recall that we’ve temporarily moved out of our flat while some brilliant builders rebuild our bathroom.</li>
<li>I find it even harder to work from the temporary accommodation than I did from home so this week I decided to take <a href="https://outlandish.com/">Outlandish</a> up on the offer of a desk.</li>
</ul>
<p><img src="/images/2020-09-13-my-desk-at-outlandish.jpg" alt="My desk at Outlandish" /></p>
<ul>
<li>The office is mostly empty but there are people from two other co-ops working from there who I enjoy spending time with. We’re doing our best to keep social distance, and I am able to walk to and from the office. I feel so much happier having a bit of company and much more productive with a clearer separation between work and home. My wrists and back are thanking me too. On balance it’s a big improvement for me but I also can’t shake the feeling that I’m part of the problem.</li>
<li>I’ve had conversations with another co-op about how best to write policies and with Space4 about how they might become a co-op in their own right. I enjoy being able to spontaneously make time for these kind of things again.</li>
<li>My computing setup got some new-term love this week too. I started using <a href="https://github.com/hlissner/doom-emacs">doom emacs</a> to replace my rag-tag old configuration files. I’d dismissed doom in the past because of its emphasis on vi-style key bindings (I’m too old to change now). But it turns out you can disable those. So far I’m impressed with the startup time and the fact that things like terminal emulation “just work”. Once I’d added some of my familiar key bindings it soon felt comfortable.</li>
<li>I’ve also started taking notes using <a href="https://www.orgroam.com/">org-roam</a>. It gives me daily note pages and easy wiki-like linking (with backlinks!). This again feels like a step up from my homegrown note taking system.</li>
<li>I’ve never been a big fan of nightclubs, but I’ve been day dreaming about going to one. Being surrounded by 100s of happy people in a confined space and not worrying about anything feels fantastical at the moment. And I want it.</li>
</ul>
Weeknotes 42020-09-06T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/09/06/weeknotes<ul>
<li>During lockdown our bath sprung a leak. The way the bath had been installed made it hard to get to and repair. And because our stopcock was seized we had to first turn off the water for the whole building and replace our stopcock. And because the stopcock for the whole building was seized we first had to turn off the water for the whole street to get that replaced. Understandably this took some time. While the leak was finally fixed (with the help of my wonderful and invested downstairs neighbour) it’s left our bathroom in a pretty bad shape. Our building insurance came through and they’ve paid for us to relocate for a couple of weeks so we’ve moved a mile away to Dartmouth Park while we have everything redecorated.</li>
<li>This would have been an opportunity to go a bit further afield for a change of scene but it’s helpful to be around when the builders have questions and N- started back at nursery on Thursday so we decided to stay close to home.</li>
<li>With N- back at nursery I had a bit of free time. I caught up with Tom for a <a href="http://www.friendsofourscafe.com/">coffee</a>. I really miss seeing friends and it was a spirit-lifting couple of hours.</li>
<li>On Friday I caught up with my colleagues for coffee and lunch on the South Bank. Like a lot of small companies we’re facing some real challenges this year, and it’s made a lot worse by not having any face-to-face time together. It was really good to catch up and despite having some tough decisions to make we had an enjoyable day.</li>
<li>I’m really enjoying Bandcamp. I love how I can discover music by following specific record labels. Buying records on the strength of the label that released them was something I did a lot of when I was younger. But since switching to streaming services for most of my listening it’s not something I’ve thought much about. I’ve been listening to a lot more music as a result. On Friday Bandcamp were passing on 100% of the royalties to artists so I picked up new releases from <a href="http://nubyagarcia.bandcamp.com/album/source">Nubya Garcia</a> (Camden-based jazz saxophonist), <a href="http://kellyleeowens.bandcamp.com/album/inner-song">Kelly Lee Owens</a> (synthy, lyrical) and <a href="http://misplacedrecordings.bandcamp.com/album/this-is-it-ep">Foreach</a> (lo-fi, minimal house).</li>
<li>I was very sad to hear that David Graeber passed away. His writing was funny, inspiring and challenging. I took for granted that I’d be able to enjoy it for many more years to come. <a href="https://novaramedia.com/2020/09/05/an-everyday-anarchist-david-graeber-1961-2020/">Paul Mason’s obituary</a> made me sad I’d never seen David speak. James reminded me of <a href="https://theanarchistlibrary.org/library/david-graeber-are-you-an-anarchist-the-answer-may-surprise-you">Are you an anarchist? The answer may surprise you</a>. It made me laugh (and thump the table a bit) again.</li>
</ul>
Weeknotes 32020-08-30T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/08/30/weeknotes<ul>
<li>We spent this week in Dorset on holiday.</li>
<li>I rented a car from Sixt (who are great) at St. Pancras station. We don’t own a car, but the hassle of collecting it, fitting two car seats, and loading in all the luggage before leaving made me slightly reconsider this position. Everything takes a lot longer with two children.</li>
<li>We visited three beaches during the week: <a href="https://en.wikipedia.org/wiki/Charmouth">Charmouth</a>, <a href="https://en.wikipedia.org/wiki/Lyme_Regis">Lyme Regis</a> and Hive beach at <a href="https://en.wikipedia.org/wiki/Burton_Bradstock">Burton Bradstock</a>.</li>
<li>All three were great, but I think I enjoyed Hive the best. It is a National Trust property and feels unspoilt, but it also has a great cafe serving really tasty (if expensive) seafood. I enjoyed a brief swim in the sea on each visit, and N- really enjoyed chasing the waves and making sand castles.</li>
<li>We were at Hive the day before <a href="https://www.bbc.co.uk/news/uk-england-dorset-53958154">a large section of the cliffs collapsed</a>.</li>
<li>The last time I visited this stretch of coastline was when I walked a stretch of the <a href="https://www.southwestcoastpath.org.uk/">South West Coast path</a> with some friends. It’s one of my favourite places.</li>
</ul>
<p><img src="/images/2020-08-30-hive-beach.jpg" alt="Hive beach" /></p>
<ul>
<li>We visited my eldest cousin Jen and her family on Sunday. It’s been far too long since I last saw her and her youngest daughter (who was just a baby when we last met) is now 8. We took her to Lyme Regis with us on Wednesday.</li>
<li>F- will be 5 months old in a couple of days. He still needs a lot of help settling to sleep, and spends a good part of every night nursing for comfort. This is exhausting for A-. At home we have an esoteric set of rituals to try and get the kids settled and maximise the amount of sleep we each get, but these are hard to keep up in unfamiliar surroundings. On the one hand I’m looking forward to this phase being over, but on the other, aw! babies.</li>
<li>We’ve got to move into yet another flat this week. Out bathroom sprang a leak during lockdown and fixing it caused so much damage that we’ve decided to get it completely refurbished. But that’s another exciting domestic story for another weeknotes. Until then, then.</li>
</ul>
Weeknotes 22020-08-23T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/08/23/weeknotes<ul>
<li>Back in London this week. I’d hoped to visit a museum or two, but as it’s the first week they’ve reopened and they all require pre-booking it’s been hard to get a slot. Realistically I wasn’t very comfortable with the idea of travelling to Kensington from where we live in Camden, so we’ll leave it for a few weeks.</li>
<li>I would like to see <a href="https://designmuseum.org/exhibitions/electronic-from-kraftwerk-to-the-chemical-brothers">Electronic: From Kraftwerk to the Chemical Brothers</a> at the Design Museum.</li>
<li>We have membership at London Zoo so I took N- there instead. It’s been ages since just the two of us went. We’ve been reading <a href="https://www.goodreads.com/book/show/36120285-the-ugly-five">The Ugly Five</a> so had fun seeing the Warthog and talking about which animals are the ugliest.</li>
<li>N- suddenly seemed to work out how to use his scooter this week which made me very proud and somewhat terrified at the same time.</li>
<li>I’ve been listening to a lot of new music this year, mostly discovered by browsing around Bandcamp and subscribing to the pitchfork reviews RSS feed. The album. <a href="https://modernarecords.bandcamp.com/album/out-of-the-valley">Out of the Valley</a> by n-So is an album I keep playing.</li>
<li>I spent a little bit of time on my rust <a href="https://github.com/chrislo/brain">side-project</a>. I wrote some code to allow me to trigger sounds immediately without sequencing them. What I’m finding interesting about this project is that it’s no longer really a programming problem it’s more of a UI challenge. I implement some features and then try and use it to make music. This always gives me a long list of new features to add or different ways to organise the interface.</li>
<li>We’ve decided to give up our office in Waterloo. This makes me sad. Not because I particularly miss the commute, or that it was a particularly pleasant physical space, rathe it’s made me think about what we’ve lost. I don’t know when I’ll next be able to sit and work in the same room as my colleagues and enjoy their company.</li>
</ul>
Weeknotes 12020-08-16T00:00:00+00:00https://chrislowis.co.uk/weeknotes/2020/08/16/weeknotes<ul>
<li>We’ve spent the week with my parents. N- is really enjoying playing in their garden and having more people to play with. During lockdown he said to me “Daddy, I want more people”. I could relate to that.</li>
<li>My parents’ garden is a joy. It’s not large but over the years they’ve developed it into separate zones (I’m not sure this is the right gardening term) and it feels much larger. I think there’s two things N- enjoys about it. Firstly it has a path looping around that he can run laps of. Secondly every time he looks at part of it, things have changed. The cats are in a different place, the fish in the pond have appeared, or there’s a new type of insect on a flower.</li>
<li>F- was born during the first week of lockdown. It’s really good to have some extra pairs of hands to help with things and the chance to take the odd break.</li>
<li>Unrelated bullet: <a href="https://www.sitandsip.co.uk/locations/wokingham/">Sit and Sip</a> in Wokingham has a good selection of beers from local brewery Siren.</li>
<li>I’ve been working on a staked chair from Chris Schwarz’s <a href="https://lostartpress.com/products/the-anarchists-design-book">The Anarchist’s Design Book</a> on and off for about a year. I’d managed to make the seat and legs, and roughed out the spindles. I’d failed a couple of times to steam bend the crest rail, and when F- arrived I had to stop working on it. With Dad’s help and access to his lathe this week we turned the tenons on each end of the spindles and finished a band-sawed crest rail. Given that we’ve been sitting on it for a couple of days, I’m going to call it a chair. It’s made from London Plane sourced by <a href="http://www.saunders-seasonings.co.uk/">Bruce Saunders</a> from a park in Euston.</li>
</ul>
<p><img src="/images/2020-08-13-chair.jpg" alt="A chair I made" /></p>
<ul>
<li>I have the material to make another and hopefully I’ll be able to get back to <a href="http://www.blackhorseworkshop.co.uk/">Blackhorse Workshop</a> before too long.</li>
<li>I stripped out some stuff from this site including Google analytics (which I never looked at) and Disqus (which annoys me everywhere else I find it). I’m hoping to <a href="https://indiewebify.me/">indiewebify</a> things a bit more following m’comrade <a href="https://jamesmead.org/blog/2020-06-27-indieweb-ifying-my-personal-website">James’s</a> example. I’m writing this post in iA Writer on my Android phone, having checked out the source using termux. This works ok, but I’d be interested to hear about alternative, carrying-a-sleeping-child-in-a-sling-friendly ways of working.</li>
</ul>
Refactoring with Emacs2017-03-24T00:00:00+00:00https://chrislowis.co.uk/2017/03/24/refactoring-with-emacs<p>We’ve been busy refactoring
the
<a href="https://github.com/alphagov/manuals-publisher/">Manuals Publisher</a>
application that’s used by the UK government to publish things
like
<a href="https://www.gov.uk/guidance/the-highway-code">The Highway Code</a>. It’s
quite an old Rails application who’s responsibilities have changed a
lot since it was first written. To make it easier for people to work
on we are pulling out some of the old code, reducing indirection, and
renaming classes to better map to the terminology used in the domain.</p>
<p>I use Emacs for most of my day-to-day development, and thought I’d
share a few tips I’ve picked up in the last few weeks. Most of these
are quite general but a few are specific to working with Ruby and
Rails applications.</p>
<h2 id="ack-in-project">Ack in project</h2>
<p>I use <a href="https://github.com/bbatsov/projectile">projectile</a> so that
Emacs knows that the files under a checked out directory all belong to
the same project. Projectile searches up the directory tree until it
finds a <code class="language-plaintext highlighter-rouge">.git</code> or <code class="language-plaintext highlighter-rouge">.projectile</code> file and then considers all code under
that directory to be part of the same project.</p>
<p>I can then run <code class="language-plaintext highlighter-rouge">M-x projectile-ag</code> to search the project for a
string. This is very useful for finding all the references to a class
before renaming, for example. I have <code class="language-plaintext highlighter-rouge">projectile-ag</code> bound to <code class="language-plaintext highlighter-rouge">C-c p a</code>.</p>
<h2 id="tag-files">Tag files</h2>
<p>Emacs has great support for TAGS files. A TAGS file contains
references to class and method definitions spread throughout the code
which are derived by statically analysing the source. I generate a
TAGS file by running</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ripper-tags -Re -f TAGS
</code></pre></div></div>
<p>which uses the <a href="https://github.com/tmm1/ripper-tags">ripper-tags</a>
gem. <code class="language-plaintext highlighter-rouge">ripper-tags</code>
uses
<a href="http://ruby-doc.org/stdlib-2.0.0/libdoc/ripper/rdoc/Ripper.html">Ripper</a> the
ruby source code parser from the Ruby standard library, so it
understands the current syntax of the language.</p>
<p>With a TAGS file in place at the root of a project <code class="language-plaintext highlighter-rouge">M-.</code> jumps to the
definition of whatever is under my cursor.</p>
<h2 id="global-search-and-replace-in-a-project">Global search and replace in a project</h2>
<p>Performing global renaming refactors in a dynamic language like Ruby
can be quite difficult. I’ve picked up a couple of tricks that can
help. The first uses <code class="language-plaintext highlighter-rouge">M-x projectile-replace-regexp</code> which allows
search and replace across the whole project, with support for Emacs
regular expressions.</p>
<h2 id="finding-references-to-code-by-running-tests">Finding references to code by running tests</h2>
<p>As Ruby is a very dynamic language, sometimes a simple search and
replace will miss references to something that has been renamed - for
example when the call site generates the method name at run time. With
a fairly comprehensive test suite, I can catch some of these
references by making a rename, running the tests
using <a href="https://github.com/pezra/rspec-mode">rspec-mode</a> and jumping
directly to the source code where the method lookup fails.</p>
<h2 id="keeping-commits-small-with-magit">Keeping commits small with Magit</h2>
<p>When several developers are all refactoring an app at the same time,
merging changes can be difficult. As James keeps reminding me, this is
made much easier if we keep our commits as small and atomic as
possible.</p>
<p><a href="https://github.com/magit/magit">Magit</a>, the git UI for Emacs, has a
couple of features that help. The first is an interface on top of <code class="language-plaintext highlighter-rouge">git
commit -p</code> which makes it easy to stage individual lines of a change.</p>
<p>The second is <code class="language-plaintext highlighter-rouge">magit-instant-fixup</code> which commits a change and then
instantly performs
a
<a href="https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History">rebase to fixup</a> the
commit into a previous one. I really like this feature - choosing the
commit to fixup into from the log is much easier than copying the SHA
on the command line.</p>
<h2 id="diff-ranges-in-magit">Diff ranges in Magit</h2>
<p>Magit also provides a convenient way to diff an arbitrary range of
commits directly from the log. Select the range and hit <code class="language-plaintext highlighter-rouge">d r</code>.</p>
<h2 id="git-time-machine">Git time machine</h2>
<p>Sometimes in order to understand how to make a refactoring it is
useful to see how the code got to the place it is
in. <a href="https://github.com/pidu/git-timemachine"><code class="language-plaintext highlighter-rouge">git-timemachine</code></a> lets
you quickly flick back through the history of a file.</p>
<h2 id="renaming-files-in-dired">Renaming files in Dired</h2>
<p>Rails has an autoloading mechanism which makes sure files are added to
the <code class="language-plaintext highlighter-rouge">LOAD_PATH</code> and required. This relies on a concordance between the
name of the class and the filename. Dired makes renaming multiple
files easy. In a Dired view of a directory, <code class="language-plaintext highlighter-rouge">M-x
wdired-change-to-wdired-mode</code> (bound to <code class="language-plaintext highlighter-rouge">C-x C-q</code> makes the view
writable. Files can be renamed and then “committed” using <code class="language-plaintext highlighter-rouge">C-c C-c</code>.</p>
<h2 id="ruby-refactor">ruby-refactor</h2>
<p>I should also
mention <a href="https://github.com/ajvargo/ruby-refactor">ruby-refactor</a> a
minor mode that makes certain simple refactorings easier. Selecting a
block of code and calling <code class="language-plaintext highlighter-rouge">M-x ruby-refactor-extract-to-method</code> is
used to create new methods, for example.</p>
<p>I did add <code class="language-plaintext highlighter-rouge">(setq ruby-refactor-add-parens t)</code> to my Emacs
configuration to ensure that parentheses were always added to method
signatures. I have only just started playing with this mode, and
haven’t trained my fingers to remember to use it yet.</p>
Web Audio Weekly has moved2016-12-05T00:00:00+00:00https://chrislowis.co.uk/2016/12/05/web-audio-weekly-has-moved<p>I’ve created a new home for Web Audio Weekly
<a href="http://www.webaudioweekly.com">www.webaudioweekly.com</a>. I’ve set up
some permanent redirects for the issues that used to live here on my
blog, however the existing RSS feed intermingles my “regular” blog
posts and WAW issues. If you want to continue reading Web Audio Weekly
using RSS please
<a href="http://www.webaudioweekly.com/feed.xml">use the new RSS feed</a>.</p>
<p>I’ll still be posting the occasional Web Audio tutorial or blog post
here on my blog, along with other topics.</p>
Notes for my talk 'A brief history of synthesis with the Web Audio API'2015-06-26T00:00:00+00:00https://chrislowis.co.uk/2015/06/26/a-brief-history-of-synthesis<h2 id="a-brief-history-of-synthesis-with-the-web-audio-api">A brief history of synthesis with the Web Audio API</h2>
<p>At <a href="http://scotlandjs.com/">ScotlandJS</a> in May this year, I gave a
talk about the history of sound synthesis with examples built using
the Web Audio API.</p>
<p>A huge thanks to <a href="https://twitter.com/jiggy_pete">Pete</a> and the entire
ScotlandJS team for hosting a fantastic conference in a beautiful
city, and for being such great hosts. The video of my talk is below,
and I’ve linked to the code samples and slides too.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/d8TCq0xLnV4" frameborder="0" allowfullscreen="true"> </iframe>
<ul>
<li><a href="https://github.com/chrislo/synth_history">Sample code</a></li>
<li><a href="https://historyofsynthesis.herokuapp.com/">Slides</a></li>
</ul>
Notes for my talk 'The Origins Of Computer Music'2015-01-26T00:00:00+00:00https://chrislowis.co.uk/2015/01/26/origins-of-computer-music<h1 id="the-origins-of-computer-music">The Origins Of Computer Music</h1>
<p>At the 1st Web Audio Conference held at IRCAM in Paris in January
2015, I gave a talk on the Origins of Computer Music.</p>
<iframe width="480" height="360" src="https://medias.ircam.fr/embed/media/x6f0394" frameborder="0" allowfullscreen=""></iframe>
<p>Here are some notes to accompany that talk.</p>
<h2 id="music-implementation-in-javascript">MUSIC implementation in JavaScript</h2>
<p>With the help of <a href="http://codon.com/">Tom Stuart</a>, I
<a href="https://github.com/chrislo/musicn.js">developed a compiler</a> for a
subset of the MUSIC V language described by Max Mathews in his 1969
book “The Technology of Computer Music” (MIT Press). This compiler
barely does enough to generate the first score in the tutorial given
by Mathews in his book but it is flexible enough to be extended to do
more if I, or any of you reading this, have the time and inclination
to do so. We wrote a parser for the language using <code class="language-plaintext highlighter-rouge">peg.js</code> and it
should be straightforward to extend that to support the missing unit
generators. The implementation of the unit generators themselves is
described in the book.</p>
<h2 id="further-reading">Further Reading</h2>
<p>In writing the talk I found the following resources very useful:</p>
<ul>
<li>“The technology of computer music”. M. V. Matthews. 1969, MIT Press.</li>
<li><a href="http://blog.frieze.com/max-mathews/">Max Mathews (1926–2011)</a>.</li>
<li><a href="http://www.mitpressjournals.org/doi/pdf/10.1162/comj.2009.33.3.9">An Interview with Max Mathews</a>. T. H. Park. Computer Music Journal, Fall 2009.</li>
<li><a href="http://www.jstor.org/discover/10.2307/3679463">Interview with Max Mathews</a>. C. Roads and Max Mathews. Computer Music Journal, Winter 1980.</li>
<li><a href="http://www.amazon.co.uk/gp/product/B000025QZF/ref=as_li_tl?ie=UTF8&camp=1634&creative=19450&creativeASIN=B000025QZF&linkCode=as2&tag=chrislowis-21&linkId=F6C5QMVAKI22HJYE">Computer Music 13. The Historical CD of Digital Sound Synthesis</a>. CD and 260-page booklet, including a reprint of Risset’s paper “Introductory Catalogue of Computer Synthesized Sounds”.</li>
<li><a href="http://www.amazon.co.uk/gp/product/0262680823/ref=as_li_tl?ie=UTF8&camp=1634&creative=19450&creativeASIN=0262680823&linkCode=as2&tag=chrislowis-21&linkId=ECVWWWBLBO7L42GA">Computer Music Tutorial</a>. Curtis Roads. MIT Press (1996).</li>
</ul>
<h2 id="thank-you">Thank you</h2>
<p>David, James, Sam, Seb and Tom provided fantastic feedback on an early
version of this talk. I’m also very grateful to Samuel for inviting me
to talk at IRCAM, an institution I’ve wanted to visit since I first
started learning about electronic music as a teenager.</p>
Creating dub delay effects with the Web Audio API2014-07-23T00:00:00+00:00https://chrislowis.co.uk/2014/07/23/dub-delay-web-audio-api
<h2 id="introduction">Introduction</h2>
<figure class="image">
<img src="/images/king_tubby.jpeg" />
<figcaption>King Tubby sat at a mixing desk [<a href="https://en.wikipedia.org/wiki/King_Tubby#mediaviewer/File:King_Tubby.jpg">credit</a>]</figcaption>
</figure>
<p>Dub music is a sub-genre of reggae. Typical of the style is the
remixing of reggae records, first stripping them down to the bass and
drums and then applying swirling, psychedelic delay effects to what’s
left. In this post we’ll take a look at the classic dub delay sound,
and try to recreate some of its character using the web audio API.</p>
<div class="highlight">
You'll need a browser that supports the Web Audio API to play the
embedded demos. Chrome 36 and above work well, but these demos rely on
cycles in the Web Audio graph which Firefox 31 has some problems with.
</div>
<h2 id="the-chop">The chop</h2>
<p>Dub delay can be applied to any element of a song, but often it’s
applied to the “chop” - an off beat, minor-chord stab played on a
keyboard or guitar. Here’s a chop</p>
<div id="chop" class="demo">
<audio src="/demos/dub_delay/chop.ogg" controls="true" loop="true">
Your browser doesn't support the audio element.
</audio>
</div>
<p>We’ve implemented it using a straight-forward HTML5 audio element
wrapping an OGG-encoded file with the controls and looping enabled</p>
<pre>
<code class="language-markup">
<audio src="/demos/dub_delay/chop.ogg" controls="true" loop="true" />
</code>
</pre>
<h2 id="delay">Delay</h2>
<p>The basis of the dub delay sound is, of course, the delay. You can
think of a delay effect as simply taking the incoming sound and
holding on to it for a pre-determined length of time, before letting
it go again. Here’s how we create a delay using the Web Audio API</p>
<pre class="eval">
<code class="language-javascript">
(function () {
var ctx = new AudioContext();
var audioElement = $('#delay audio')[0];
audioElement.addEventListener('play', function() {
var source = ctx.createMediaElementSource(audioElement);
var delay = ctx.createDelay();
delay.delayTime.value = 0.5;
source.connect(delay);
source.connect(ctx.destination);
delay.connect(ctx.destination);
});
})();
</code>
</pre>
<p>First we create an
<a href="https://webaudio.github.io/web-audio-api/#the-audiocontext-interface">AudioContext</a>
to hold the nodes in our processing graph. The first node in our graph
is the
<a href="https://webaudio.github.io/web-audio-api/#the-mediaelementaudiosourcenode-interface">MediaElementSource</a>
node, which allows routes the audio from a HTML5 audio element into
the graph. We have to wait for the <code class="language-plaintext highlighter-rouge">play</code> event to fire to
be sure that the audio has loaded and has been decoded ready to hand
off to the audio context.</p>
<p>We then create a
<a href="https://webaudio.github.io/web-audio-api/#the-delaynode-interface">Delay</a>
node, with a delay time of 0.5 seconds. Finally we connect the delay
to the destination. Note here that we’ve also connected the source
itself to the destination, if we don’t do that, we’ll just here the
delayed version of the chop, and not the original - this way, we hear
both:</p>
<div id="delay" class="demo">
<audio src="/demos/dub_delay/chop.ogg" controls="true" loop="true">
Your browser doesn't support the audio element.
</audio>
</div>
<h2 id="feedback">Feedback</h2>
<p>The delay on its own is a good start, but somehow it sounds a little
too sterile. We need to add some feedback to start getting the
swirling, psychedelic sounds of dub. To recreate this sound, we have
to understand a little bit about how dub producers created it.</p>
<p>In an analogue studio, each mixing desk channel typically has an
“auxiliary send” which allows some of the channel’s audio to be routed
to an external effects unit, for example a analogue tape delay. If the
return from that unit is sent back to another channel of the mixing
desk - that channel too has an auxiliary send. By increasing the send
on the return channel, the delayed sound can be routed through the
delay unit again, and again, and again… creating what is known as
feedback. Here’s how we do that in code</p>
<pre class="eval">
<code class="language-javascript">
(function() {
var ctx = new AudioContext();
var audioElement = $('#feedback audio')[0];
audioElement.addEventListener('play', function(){
var source = ctx.createMediaElementSource(audioElement);
var delay = ctx.createDelay();
delay.delayTime.value = 0.5;
var feedback = ctx.createGain();
feedback.gain.value = 0.8;
delay.connect(feedback);
feedback.connect(delay);
source.connect(delay);
source.connect(ctx.destination);
delay.connect(ctx.destination);
});
})();
</code>
</pre>
<p>This is very similar to the code we saw above, but we’ve introduced a
<a href="https://webaudio.github.io/web-audio-api/#the-gainnode-interface">Gain</a> node between the output of the delay node and the input of
the delay node. Connecting the delay node to itself is enough to
create the feedback loop, but without the gain node we’d be feeding
100% of the delayed sound back into the delay - creating a delay that
grows forever. Introducing the gain node allows us to control
this. Here’s how it sounds</p>
<div id="feedback" class="demo">
<audio src="/demos/dub_delay/chop.ogg" controls="true" loop="true">
Your browser doesn't support the audio element.
</audio>
</div>
<p>Notice as the audio element itself loops, the tail of the feedback
keeps playing - the original source and the feedback combine in
interesting rhythmic ways depending on the amount of feedback and the
length of the delay. This is a characteristic sound of dub.</p>
<h2 id="degradation-in-the-audio-chain">Degradation in the audio chain</h2>
<p>When working with the Web Audio API we are manipulating digital
signals. Each echo in our feedback chain is a perfect copy of the
original, just slightly quieter. It’s as close to “perfect” as we can
get. Dub producers work with much less perfect equipment - analogue
mixing desks and analogue-tape based delay units. Each time the signal
passes through that processing chain it loses some fidelity - and this
contributes to the sound of the dub delay. Creating a clone of this
sound using digital signals is something of a holy grail, but to start
you on that journey, lets introduce something to “dirty” up the
feedback.</p>
<pre class="eval">
<code class="language-javascript">
(function () {
var ctx = new AudioContext();
var audioElement = $('#filter audio')[0];
audioElement.addEventListener('play', function(){
var source = ctx.createMediaElementSource(audioElement);
var delay = ctx.createDelay();
delay.delayTime.value = 0.5;
var feedback = ctx.createGain();
feedback.gain.value = 0.8;
var filter = ctx.createBiquadFilter();
filter.frequency.value = 1000;
delay.connect(feedback);
feedback.connect(filter);
filter.connect(delay);
source.connect(delay);
source.connect(ctx.destination);
delay.connect(ctx.destination);
});
})();
</code>
</pre>
<p>Here, we’ve introduced a
<a href="https://webaudio.github.io/web-audio-api/#the-biquadfilternode-interface">BiquadFilter</a>
node. By default, this is a low-pass filter, which filters out
frequencies above the <code class="language-plaintext highlighter-rouge">frequency</code> parameter. This is a simple way of
adding some interest and it mimics the way an analogue signal chain
would have a bigger effect on higher frequencies than lower ones.</p>
<div id="filter" class="demo">
<audio src="/demos/dub_delay/chop.ogg" controls="true" loop="true">
Your browser doesn't support the audio element.
</audio>
</div>
<p>As each echo decays away it also loses some of its high frequency
content.</p>
<h2 id="putting-it-all-together-with-knobs-on">Putting it all together with knobs on!</h2>
<p>Of course, most of the creative uses of dub delay don’t involve just
the static application of the effect to the incoming sound. Instead
the producer plays the equipment like an instrument, allowing the
feedback to rise and dominate, stopping the original source to just
play the echos, and adjusting the delay time and other filters in
creative ways. Let’s try hooking up some simple sliders to some of the
parameters of our dub delay effect.</p>
<pre class="eval">
<code class="language-javascript">
(function () {
var ctx = new AudioContext();
audioElement = $('#sliders audio')[0]
audioElement.addEventListener('play', function(){
source = ctx.createMediaElementSource(audioElement);
delay = ctx.createDelay();
delay.delayTime.value = 0.5;
feedback = ctx.createGain();
feedback.gain.value = 0.8;
filter = ctx.createBiquadFilter();
filter.frequency.value = 1000;
delay.connect(feedback);
feedback.connect(filter);
filter.connect(delay);
source.connect(delay);
source.connect(ctx.destination);
delay.connect(ctx.destination);
});
var controls = $("div#sliders");
controls.find("input[name='delayTime']").on('input', function() {
delay.delayTime.value = $(this).val();
});
controls.find("input[name='feedback']").on('input', function() {
feedback.gain.value = $(this).val();
});
controls.find("input[name='frequency']").on('input', function() {
filter.frequency.value = $(this).val();
});
})();
</code>
</pre>
<p>Now, try adjusting the values of the parameters and experiment with
how they sound together. Starting and stopping the audio element is
also possible, since the effects are happening “inside” the audio
context.</p>
<div id="sliders" class="demo">
<audio src="/demos/dub_delay/chop.ogg" controls="true" loop="true">
Your browser doesn't support the audio element.
</audio>
<div class="controls">
delayTime: <input type="range" name="delayTime" min="0" max="1" value="0.5" step="0.05" />
feedback: <input type="range" name="feedback" min="0" max="0.95" value="0.8" step="0.05" />
cutoff freq: <input type="range" name="frequency" min="0" max="4000" value="1000" step="100" />
</div>
</div>
<h2 id="summary">Summary</h2>
<p>We’ve used the Web Audio API to create a simple recreation of some
classic dub delay sounds. Where next?</p>
<ul>
<li>You might have noticed a small artifact when adjusting the delay
time. The specification
<a href="https://webaudio.github.io/web-audio-api/#the-delaynode-interface">states</a>
“When the delay time is changed, the implementation must make the
transition smoothly, without introducing noticeable clicks or
glitches to the audio stream”. When a change is made to the delay
time parameter, the implementations make a smooth transition to the
new value (called “dezippering”). In this case the more rapid change
in time creates an artifact, which an analogue tape recorder
wouldn’t have due to the mechanical constraints of the device on the
speed of the change. You can experiment with using
<code class="language-plaintext highlighter-rouge">.setValueAtTime(value, 0)</code> to remove the dezippering effect.</li>
<li><a href="http://www.soundonsound.com/sos/may12/articles/designer-delay.htm#para6">Ping-pong delay</a>
is an effect where each echo is sent alternately to the left and
right channels. You sometimes here this effect in use, and it is possible to implement it using an extra delay node together with the <a href="https://webaudio.github.io/web-audio-api/#the-channelsplitternode-interface">channel splitter</a> and <a href="https://webaudio.github.io/web-audio-api/#the-channelmergernode-interface">channel merger</a> nodes.</li>
</ul>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.soundonsound.com/sos/feb10/articles/analoguewarmth.htm">Analogue Warmth: The Sound Of Tubes, Tape & Transformers</a>. A comprehensive take on the subject of analogue “warmth” - a rabbit hole you might be tempted to lose yourself in.</li>
<li><a href="http://vimeo.com/860391">Dubbing out in Ableton Live</a>. An overview of how to create dub delay effects using the desktop software Abelton Live. All of the techniques in this video would be possible with the Web Audio API with enough effort.</li>
</ul>
<h2 id="thanks">Thanks</h2>
<p>It took me a long time to write this post, as I hit some pretty odd
bugs along the way. A huge thanks to <a href="http://codon.com/">Tom</a>,
<a href="http://gofreerange.com/">Chris and James</a> for providing advice, space
and for putting up with my noise.</p>
<p>Also thanks to <a href="https://plus.google.com/+ChrisWilson">Chris Wilson</a>
for pointing out the issue with artifacts/dezippering.</p>
<!-- LocalWords: API contentfor href stylesheet src init pre eval
-->
<!-- LocalWords: evaluatableElements el textContent px moz webkit
-->
<!-- LocalWords: img figcaption center color FFFBCC endcontentfor
-->
<!-- LocalWords: mediaviewer OGG lt javascript ctx AudioContext
-->
<!-- LocalWords: audioElement addEventListener createDelay gainnode
-->
<!-- LocalWords: createMediaElementSource audiocontext delaynode
-->
<!-- LocalWords: MediaElementSource mediaelementaudiosourcenode
-->
<!-- LocalWords: createGain createBiquadFilter BiquadFilter Ableton
-->
<!-- LocalWords: biquadfilternode delayTime splitter Abelton
-->
<!-- LocalWords: channelsplitternode channelmergernode
-->
Interview with Adrian Holovaty of Soundslice2014-05-30T00:00:00+00:00https://chrislowis.co.uk/2014/05/30/interview-adrian-holovaty-soundslice<p>I recently spoke to
<a href="https://twitter.com/adrianholovaty">Adrian Holovaty</a> the founder of
<a href="http://www.soundslice.com/">Soundslice</a>, an interactive sheet music
application, about how it was built and his experiences of using the
Web Audio API for commercial development.</p>
<h3 id="could-you-tell-me-a-bit-about-soundslice-what-the-company-does-and-what-applications-youve-built">Could you tell me a bit about Soundslice, what the company does and what applications you’ve built?</h3>
<p>Soundslice is a web site that helps musicians learn songs. Our
ultimate goal is to be the easiest and highest-quality way to learn
whatever song you’d like, on whatever instrument.</p>
<p>As part of this, we’ve built a web-based sheet-music and tablature
player that makes it easy to explore a piece of music. It syncs the
music notation with real recordings, so that you can hear how the
music is supposed to sound as notes are highlighted, and it lets you
<a href="http://www.soundslice.com/scores/auld-lang-syne/">loop, slow down and navigate the audio via the notation</a>.</p>
<iframe width="420" height="315" src="https://www.youtube.com/embed/tq1go7Ld1Q4" frameborder="0" allowfullscreen="true"> </iframe>
<p>On the business side, we sell high-quality music transcriptions in
this format, and we license our technology to other companies. I
believe we’re one of the first companies to build production-quality
sites - stuff that people actually pay money for! - using the Web
Audio API.</p>
<p>Aside from the Web Audio API, we’re doing some quite advanced stuff
with HTML5’s <code class="language-plaintext highlighter-rouge"><canvas></code> , rendering music notation and tablature in an
efficient, cross-platform way.</p>
<h3 id="how-have-you-used-web-audio-in-the-application">How have you used Web Audio in the application?</h3>
<p>We use the API in two ways: “real” audio playback and “synthetic”
playback. In the Soundslice interface, you can toggle the audio source
to be either a real recording (an MP3) or a synthetic version
(generated from the notation, MIDI-style).</p>
<p>For the real recordings, we use the Web Audio API to play and loop it
with precise timing. We also use it to slow down the audio without
changing pitch, using <code class="language-plaintext highlighter-rouge">ScriptProcessorNodes</code>.</p>
<p>For the synthetic audio, we use the Web Audio API to splice together
instrument samples in real time, based on whatever notes are in the
sheet music. For this, I use a single audio file that has every
possible note in it – the same concept as CSS image sprites.</p>
<p>Fun fact: our “real” audio player actually has <em>three</em> audio backends,
which are chosen based on the browser’s capabilities – Web Audio API,
HTML5 <code class="language-plaintext highlighter-rouge"><audio></code> and a Flash fallback. Obviously we prefer Web Audio
API, but as anybody in this space knows, consistent audio performance
across browsers still requires a lot of hoop-jumping.</p>
<h3 id="what-has-been-good-about-using-the-api-whats-been-bad">What has been good about using the API, what’s been bad?</h3>
<p>The best part about it is the precision and control. Looping can have
perfect or near-perfect timing, you can easily play multiple audio
sources at once, and you can use <code class="language-plaintext highlighter-rouge">ScriptProcessorNodes</code> to futz with
the content of the audio.</p>
<p>The bad things: lack of support in all browsers (not really the API’s
fault), and, to some extent, performance when doing crazy things in
<code class="language-plaintext highlighter-rouge">ScriptProcessorNodes</code> (again, not really the API’s fault). Plus there are a
<a href="http://stackoverflow.com/questions/10365335/">few small bugs I’ve needed to hack around</a>.</p>
<p>Oh, and I’d love for there to be a built-in way to adjust playback
speed without changing pitch – the earlier <code class="language-plaintext highlighter-rouge"><audio></code> tag API has
that, so I don’t see why Web Audio API shouldn’t. It would make my job
a lot easier, and Soundslice would perform better on slower machines
if the slowdown algorithms were implemented directly in the browser.</p>
<h3 id="how-does-developing-with-web-audio-compare-to-developing-for-other-platforms-or-technologies">How does developing with Web Audio compare to developing for other platforms or technologies?</h3>
<p>I can only compare it to the HTML5 <code class="language-plaintext highlighter-rouge"><audio></code> tag API and
Flash/Actionscript. Overall, the Web Audio API is the nicest of the
three. The HTML5 API starts to feel very hacky when you have multiple
sounds playing at the same time, though its “playbackRate” API is
convenient. I don’t remember liking the Actionscript APIs very much
when I was using them a few years ago for an earlier version of
Soundslice.</p>
<h3 id="if-you-could-make-changes-to-the-api-or-to-audio-in-the-browser-in-general-what-would-you-do">If you could make changes to the API or to audio in the browser in general, what would you do?</h3>
<p>Number one priority would be to add a built-in way of changing speed
without altering pitch, just as the HTML5 <code class="language-plaintext highlighter-rouge"><audio></code> API already has.</p>
<p>Number two priority would be to add a callback/event when a piece of
audio
<a href="http://updates.html5rocks.com/2012/01/Web-Audio-FAQ">has stopped playing</a>. Currently
you have to use setTimeout!</p>
<p>Number three priority would be to fix playback bugs like the one I
mentioned above.</p>
<p>And of course, magically upgrading every browser in the world to one
that has a stable implementation of the API would be lovely.</p>
Writing tests for the Web Audio API2014-04-30T00:00:00+00:00https://chrislowis.co.uk/2014/04/30/testing-web-audio<h2 id="introduction">Introduction</h2>
<p>In this post I want to give a brief overview of how you can help the
adoption of the Web Audio API by writing tests for the W3C’s official
test suite. Writing tests helps the adoption of the API in three ways</p>
<ol>
<li>It makes it easier for a wider range of browser vendors to support
the API</li>
<li>It makes it easier for an individual vendor to implement the
standard correctly</li>
<li>It makes it easier for new additions to the specification to be
included and approved</li>
</ol>
<p>The test suite is still very incomplete, and there’s a lot of room for
improvement of the testing process as well as increasing the test
coverage. But, if you know a little JavaScript, it’s easy to help, so
let’s get started!</p>
<h2 id="getting-started">Getting started</h2>
<p>The W3C’s test suite for the “web platform” (the suite of
technologies, Web Audio included, that make up the modern web) is
<a href="https://github.com/w3c/web-platform-tests">on GitHub</a>. So go ahead
and clone it</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git clone https://github.com/w3c/web-platform-tests
</code></pre></div></div>
<p>Or, if you think you might contribute, you may find it easier to fork
the repository into your own GitHub account, and clone your fork.</p>
<p>The repository requires git submodules, which you can update within
your checkout</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git submodule update --init --recursive
</code></pre></div></div>
<p>The repo has the
<a href="https://github.com/w3c/web-platform-tests#running-the-tests">latest instructions for getting started</a>.</p>
<h2 id="running-a-web-audio-test-locally">Running a Web Audio test locally</h2>
<p>You need to arrange for the contents of the repository to be served up
by a local webserver. There’s
<a href="https://github.com/w3c/web-platform-tests#running-the-tests">some instructions</a>
for doing that using the included <code class="language-plaintext highlighter-rouge">serve.py</code> python script and some
simple edits to your <code class="language-plaintext highlighter-rouge">/etc/hosts</code> file - or you may have your own
preferred way. Once you have the server up and running, try and run
the test for the Web Audio API <code class="language-plaintext highlighter-rouge">GainNode</code>, on my machine it was at
<code class="language-plaintext highlighter-rouge">http://web-platform.test:55521/webaudio/the-audio-api/the-gainnode-interface/test.html</code>.</p>
<p>The test suite is also mirrored to <code class="language-plaintext highlighter-rouge">w3c-test.org</code>, including the
<a href="http://w3c-test.org/webaudio/the-audio-api/the-gainnode-interface/test.html">GainNode test</a>
and the rest of the web audio tests.</p>
<h2 id="understanding-the-web-audio-api-tests">Understanding the Web Audio API tests</h2>
<p>The Web Audio API test suite is very minimal at the moment, but that’s
where you can help.</p>
<p>The Web Audio API is under development, so that
<a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html">latest version of the editor’s draft of the specification</a>
is what we should be writing our tests against. Go and take a look at
the specification if you’re not familiar with it.</p>
<p>Notice that the specification is grouped into sections, (for example
<code class="language-plaintext highlighter-rouge">§4.7 The GainNode interface</code>). The directory structure of the tests
repo reflects this structure, so we have
<code class="language-plaintext highlighter-rouge">/webaudio/the-audio-api/the-gainnode-interface</code>.</p>
<p>Tests come in two different flavours:</p>
<ul>
<li><em>Functional tests</em>. These tests assert that the audio processing
performed by a certain node does the right thing. For example, we
might assert that the output of an <code class="language-plaintext highlighter-rouge">OscillatorNode</code> when the
<code class="language-plaintext highlighter-rouge">OscillatorType</code> is <code class="language-plaintext highlighter-rouge">sine</code> actually produces a sine wave at the
correct frequency.</li>
<li><em>IDL tests</em>. These tests assert that the interface presented to the
programmer conforms to that written in the specification, so, for
example that, <code class="language-plaintext highlighter-rouge">AudioContext</code> has a method <code class="language-plaintext highlighter-rouge">createOscillator</code>.</li>
</ul>
<p>We’ll look at both of these types of tests in turn.</p>
<h3 id="writing-functional-tests">Writing functional tests</h3>
<p>Functional tests assert that a audio processing node performs its
processing correctly. The process for writing a test is as follows:</p>
<ol>
<li>Find an area of the
<a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html">specification</a>
that doesn’t have tests.</li>
<li>Read the specification and see if it could be tested as written. If
you feel a test cannot be written against the current version of
the spec, for example if there’s not enough information in the spec
to determine precisely what the output should be, that’s great! You
can help to <a href="#improve_the_specification">improve the spec</a>.</li>
<li>Write the test</li>
</ol>
<p>Let’s look at step 3 in more detail. Tests are written using
<a href="http://testthewebforward.org/docs/testharness-tutorial.html">the W3C’s testharness.js framework</a>. Take
a look at that documentation to familiarise yourself.</p>
<p>Don’t reinvent the wheel. If you’re considering writing a functional
test for a node, both the Mozilla and Webkit source code already have
a number of tests that you can port over, or use for inspiration:</p>
<ul>
<li><a href="https://github.com/WebKit/webkit/tree/master/LayoutTests/webaudio">Web Audio API tests in the Webkit repository</a></li>
<li><a href="https://mxr.mozilla.org/mozilla-central/source/content/media/webaudio/test/">Web Audio API tests in the Mozilla repository</a></li>
</ul>
<p>As an example, consider the <code class="language-plaintext highlighter-rouge">GainNode</code> test in the W3C test
suite. You’ll find it at
<code class="language-plaintext highlighter-rouge">/webaudio/the-audio-api/the-gainnode-interface/test.html</code>, or <a href="http://w3c-test.org/webaudio/the-audio-api/the-gainnode-interface/test.html">here on w3c-test.org</a>.</p>
<p>This test works as follows:</p>
<ol>
<li>Create an <code class="language-plaintext highlighter-rouge">AudioBuffer</code> with a series of sine wave ‘notes’ of
gradually decreasing amplitude. This is the expected output.</li>
<li>Recreate this using a <code class="language-plaintext highlighter-rouge">GainNode</code> with gradually decreasing <code class="language-plaintext highlighter-rouge">gain</code>
value.</li>
<li>Record the output of the audio graph created in 2. in an
<code class="language-plaintext highlighter-rouge">offlineAudioContext</code>.</li>
<li>Assert that recorded output matches the expected output.</li>
</ol>
<p>This test was based on the
<a href="https://github.com/WebKit/webkit/blob/master/LayoutTests/webaudio/gain.html">corresponding test of the GainNode in the Webkit test suite</a>,
but uses a generated buffer rather than a WAVE file as the expected
output. The reason for this is to allow the tests to run faster than
real time. If we were to create a node graph in a regular
<code class="language-plaintext highlighter-rouge">AudioContext</code>, and then capture the output in a buffer using a
<code class="language-plaintext highlighter-rouge">scriptProcessorNode</code>, for example, the test would take at least as
long to run as the audio generated. Using an <code class="language-plaintext highlighter-rouge">offlineAudioContext</code>
allows the implementation to generate the output as fast as it can.</p>
<p>In some cases it will be impossible to use <code class="language-plaintext highlighter-rouge">offlineAudioContext</code>, such
as when writing tests for the various streaming sources.</p>
<h3 id="writing-idl-tests">Writing IDL tests</h3>
<p>The IDL tests generate automated tests from the
<a href="http://www.w3.org/TR/WebIDL/">Web IDL</a> descriptions of the interfaces
of each of the functions provided by the Web Audio API, and used in
the
<a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html">specification</a>.</p>
<p>In the W3C test suite we have a Ruby script (at
<code class="language-plaintext highlighter-rouge">/webaudio/refresh_idl.rb</code>) which extracts the IDL descriptions from
the specification, and updates the corresponding tests. It’s still
quite a manual process at the moment, and I would appreciate any
improvements you can suggest.</p>
<h2 id="contributing-your-test">Contributing your test</h2>
<p>The W3C test suite accepts contributions in the form of GitHub pull
requests. Each pull request has to be
<a href="http://testthewebforward.org/docs/review-process.html">reviewed by a peer</a>. At
the moment, I am the test coordinator for the Web Audio tests, so it
is likely to be me that does the review and merge, but anyone who
would like to help will be very welcome.</p>
<p>If you need any help, please get in touch with me in the comments
below, on the
<a href="http://lists.w3.org/Archives/Public/public-audio/">public audio mailing list</a>
or by raising an issue with the <code class="language-plaintext highlighter-rouge">webaudio</code> label in GitHub.</p>
<h2 id="improve-the-specification">Improve the specification</h2>
<p>When starting to write a test for a part of the specification you may
encounter a situation where there’s not enough information in the
spec to determine precisely what the output should be. In these
cases you can help to improve the specification:</p>
<ul>
<li>Open an issue against the spec at the
<a href="https://github.com/WebAudio/web-audio-api/issues?state=open">specification’s GitHub repo</a>.
Include the test case you have written so far, if you can - it’s
easier to discuss concrete problems that are illustrated by code</li>
<li>You may need to ask the editors and other members on the W3C’s
<a href="http://lists.w3.org/Archives/Public/public-audio/">public audio mailing list</a>
for help. They really appreciate these questions as they do help to
clarify complicated areas of the specification.</li>
</ul>
Synthesis with the Web Audio API - Envelopes2013-06-17T00:00:00+00:00https://chrislowis.co.uk/2013/06/17/synthesis-web-audio-api-envelopes
<p>When we looked at
<a href="http://blog.chrislowis.co.uk/2013/06/05/playing-notes-web-audio-api.html">monophonic synthesis</a>
in an earlier post on this blog the notes we played were produced by
simply turning a Sine wave on and off using a Voltage Controlled
Amplifier (VCA). In this post, we’ll look at how to make this sound a
little bit more interesting by varying the amplitude of the oscillator
over time by applying what is known as an “envelope” to the VCA.</p>
<p>In the earlier monophonic synthesis example we directly modified the
parameters of the nodes in the <code class="language-plaintext highlighter-rouge">AudioContext</code> graph based on key
presses. As the number of nodes and the complexity of the synthesis
increases though, the code can start to get unwieldy. Instead let’s
start by introducing some reusable objects that can be combined
together.</p>
<h2 id="vco">VCO</h2>
<p>We start with a function that can be used to create Oscillators.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">VCO</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">context</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">VCO</span><span class="p">(){</span>
<span class="k">this</span><span class="p">.</span><span class="nx">oscillator</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">createOscillator</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">oscillator</span><span class="p">.</span><span class="nx">type</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">sawtooth</span><span class="dl">'</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">setFrequency</span><span class="p">(</span><span class="mi">440</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">oscillator</span><span class="p">.</span><span class="nx">start</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">oscillator</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">oscillator</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">frequency</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">frequency</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">that</span><span class="p">.</span><span class="nx">setFrequency</span><span class="p">(</span><span class="nx">frequency</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">};</span>
<span class="nx">VCO</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">setFrequency</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">frequency</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">oscillator</span><span class="p">.</span><span class="nx">frequency</span><span class="p">.</span><span class="nx">setValueAtTime</span><span class="p">(</span><span class="nx">frequency</span><span class="p">,</span> <span class="nx">context</span><span class="p">.</span><span class="nx">currentTime</span><span class="p">);</span>
<span class="p">};</span>
<span class="nx">VCO</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">connect</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">input</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">VCO</span><span class="p">;</span>
<span class="p">})(</span><span class="nx">context</span><span class="p">);</span></code></pre></figure>
<p>The Web Audio API calls in this function should look familiar. We
create an sawtooth <code class="language-plaintext highlighter-rouge">OscillatorNode</code>, and tell it to start immediately.
However the VCO object created by this class also does two new things.
In the lines</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">...</span>
<span class="k">this</span><span class="p">.</span><span class="nx">input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">oscillator</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">oscillator</span><span class="p">;</span>
<span class="p">...</span>
<span class="nx">VCO</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">connect</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">input</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">}</span></code></pre></figure>
<p>we’re specifying what the ‘input’ and ‘output’ nodes are for our
object, and defining a <code class="language-plaintext highlighter-rouge">connect</code> method that can behave in the same
way as ‘native’ Web Audio nodes. In this way, our custom VCO can be a
composition of a number of Web Audio Nodes but we’re still able to
write code such as</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">myOscillator</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VCO</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">gainNode</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">createGain</span><span class="p">();</span>
<span class="nx">myOscillator</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">gainNode</span><span class="p">);</span></code></pre></figure>
<p>We’ll use this pattern again in the future.</p>
<p>The second new pattern in this function is the use of events to set
parameters</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="p">...</span>
<span class="kd">var</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">frequency</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">frequency</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">that</span><span class="p">.</span><span class="nx">setFrequency</span><span class="p">(</span><span class="nx">frequency</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">...</span></code></pre></figure>
<p>This provides a simple way of triggering multiple oscillator notes
from a single keypress, for example.</p>
<h2 id="envelopes">Envelopes</h2>
<p>An Envelope Generator on an analogue synth will generate a voltage
that smoothly varies over time. The variation begins when the
generator receives a trigger (usually from pressing a key on the
keyboard), and the smoothly varying voltage that is created can then
be connect to, for example a Voltage Controlled Amplifier, to vary the
amplitude of an oscillator with each key press. By doing this, we can
turn the monotonous on-off sound of an oscillator into something that
sounds a little bit more like the percussive ‘twang’ of a guitar
string, for example.</p>
<p>As the Envelope Generator simply generates a varying voltage, we can
in fact use this voltage to control any part of our synth - for
example, the frequency of an oscillator, or the cutoff frequency of a
filter. Most commercial analogue synthesisers allowed envelopes to be
used in this way, and the more expressive the synth the more options
it had.</p>
<p>Luckily, in the Web Audio API almost every parameter of every Node
implements the <code class="language-plaintext highlighter-rouge">AudioParam</code> interface, which allows it to be varied in
limitless ways.</p>
<p>To begin with though, let’s consider a vary simple envelope generator,
one that generates an Attack-Release (AR) envelope. Here’s a sketch of
the voltage generated by this generator as a function of time</p>
<p><img src="/images/ar-envelope.png" width="400px" /></p>
<p>When the generator receives a trigger, the voltage starts at zero,
rises linearly to a maximum in <code class="language-plaintext highlighter-rouge">TA</code> seconds, then immediately falls
to zero in <code class="language-plaintext highlighter-rouge">TD</code> seconds. The first portion of the curve is called the
“attack”, the second part, the “release”.</p>
<p>The code for our Envelope Generator looks like this</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">EnvelopeGenerator</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">context</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">EnvelopeGenerator</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">attackTime</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">releaseTime</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">that</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">gateOn</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">that</span><span class="p">.</span><span class="nx">trigger</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">setAttack</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">that</span><span class="p">.</span><span class="nx">attackTime</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">$</span><span class="p">(</span><span class="nb">document</span><span class="p">).</span><span class="nx">bind</span><span class="p">(</span><span class="dl">'</span><span class="s1">setRelease</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">that</span><span class="p">.</span><span class="nx">releaseTime</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">};</span>
<span class="nx">EnvelopeGenerator</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">trigger</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">now</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">currentTime</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">param</span><span class="p">.</span><span class="nx">cancelScheduledValues</span><span class="p">(</span><span class="nx">now</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">param</span><span class="p">.</span><span class="nx">setValueAtTime</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">now</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">param</span><span class="p">.</span><span class="nx">linearRampToValueAtTime</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nx">now</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attackTime</span><span class="p">);</span>
<span class="k">this</span><span class="p">.</span><span class="nx">param</span><span class="p">.</span><span class="nx">linearRampToValueAtTime</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">now</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attackTime</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">releaseTime</span><span class="p">);</span>
<span class="p">};</span>
<span class="nx">EnvelopeGenerator</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">connect</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">param</span><span class="p">)</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">param</span> <span class="o">=</span> <span class="nx">param</span><span class="p">;</span>
<span class="p">};</span>
<span class="k">return</span> <span class="nx">EnvelopeGenerator</span><span class="p">;</span>
<span class="p">})(</span><span class="nx">context</span><span class="p">);</span></code></pre></figure>
<p>We have seen the event binding code before. The interesting part here
is the <code class="language-plaintext highlighter-rouge">trigger</code> method. Taking it line by line, we first establish
the current time</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">now</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">currentTime</span><span class="p">;</span></code></pre></figure>
<p>In the Web Audio API, the <code class="language-plaintext highlighter-rouge">context</code> has it’s own timer, and a clock
which advances from the point the context is created. This clock is
vital for accurate timing of audio events, and for maintaining the
stability of those audio events even when, for example, there is a lot
of other activity in the browser window.</p>
<p>Any parameter, such as <code class="language-plaintext highlighter-rouge">gainNode.gain</code>, that implements the
<code class="language-plaintext highlighter-rouge">AudioParam</code> interface can have its value scheduled at a point in the
future with reference to the <code class="language-plaintext highlighter-rouge">currentTime</code> of the context. In case we
have already scheduled some changes to the value of the parameter we
have connected to the <code class="language-plaintext highlighter-rouge">EnvelopeGenerator</code> we first cancel those
scheduled events when we receive a new trigger</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">this</span><span class="p">.</span><span class="nx">param</span><span class="p">.</span><span class="nx">cancelScheduledValues</span><span class="p">(</span><span class="nx">now</span><span class="p">);</span></code></pre></figure>
<p>Our AR envelope starts at zero, so we first schedule the value of the
parameter to zero immediately</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">this</span><span class="p">.</span><span class="nx">param</span><span class="p">.</span><span class="nx">setValueAtTime</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">now</span><span class="p">);</span></code></pre></figure>
<p>The attack portion of our AR envelope is a linear increase to the
maximum value (1, in this case). The <code class="language-plaintext highlighter-rouge">linearRampToValueAtTime</code> method
handles the smooth transition for us, we just need to specify the
value to ramp to, and the amount of time the ramp portion should last</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">this</span><span class="p">.</span><span class="nx">param</span><span class="p">.</span><span class="nx">linearRampToValueAtTime</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nx">now</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attackTime</span><span class="p">);</span></code></pre></figure>
<p>And finally, we call the same method again to ramp the value to zero
at the end of the envelope. Remember, we’re scheduling all of these
changes for the future, so we need to specify the time at which the
parameter should be ramped to zero, which is the sum of the attack
time and the release time.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="k">this</span><span class="p">.</span><span class="nx">param</span><span class="p">.</span><span class="nx">linearRampToValueAtTime</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">now</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">attackTime</span> <span class="o">+</span> <span class="k">this</span><span class="p">.</span><span class="nx">releaseTime</span><span class="p">);</span></code></pre></figure>
<p>The <code class="language-plaintext highlighter-rouge">linearRampToValueAtTime</code> method is only one of several methods
which can be used to create a variety of envelope shapes. Take a
<a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#methodsandparams-AudioParam">look at the spec</a>
for details.</p>
<h2 id="putting-it-all-together">Putting it all together</h2>
<p>To add this envelope to our monophonic synth, we need to re-write the
VCA to expose the <code class="language-plaintext highlighter-rouge">gain</code> parameter of the <code class="language-plaintext highlighter-rouge">gainNode</code> through an
interface and then connect this up to the envelope. The VCA code looks
like</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">VCA</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">context</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">VCA</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">gain</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">createGain</span><span class="p">();</span>
<span class="k">this</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">input</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">gain</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">gain</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">amplitude</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">gain</span><span class="p">;</span>
<span class="p">};</span>
<span class="nx">VCA</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">connect</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">node</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="dl">'</span><span class="s1">input</span><span class="dl">'</span><span class="p">))</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">input</span><span class="p">);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">output</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">node</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">}</span>
<span class="k">return</span> <span class="nx">VCA</span><span class="p">;</span>
<span class="p">})(</span><span class="nx">context</span><span class="p">);</span></code></pre></figure>
<p>And the connections between our objects is quite simple</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">vco</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VCO</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">vca</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">VCA</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">envelope</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">EnvelopeGenerator</span><span class="p">;</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vca</span><span class="p">);</span>
<span class="nx">envelope</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vca</span><span class="p">.</span><span class="nx">amplitude</span><span class="p">);</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">destination</span><span class="p">);</span></code></pre></figure>
<p>Try out this
<a href="/demos/ar-envelope-synth/synth.html">simple monosynth with an AR envelope</a>.
I’ve used the <a href="http://anthonyterrien.com/knob/">jQuery Knob</a> library
to allow the attack and release of the envelope to be set.
<a href="https://github.com/chrislo/web-audio-synth-experiments/tree/ar-envelope-blog-post">Take a look at the code</a>
of the demo to see how that’s done.</p>
<p>There are many more sophisticated envelope types that were commonly
used on classic analogue synths, and many ways in which envelopes
could be used to creatively modify sounds. We’ll look at this and more
in future posts.
<a href="http://feeds.feedburner.com/ChrisLowis">Subscribe to the RSS feed</a> or
<a href="http://twitter.com/chrislowis">follow me on Twitter</a> to find out when
I’ve written some more!</p>
<h2 id="references">References</h2>
<ul>
<li><a href="http://www.soundonsound.com/sos/nov99/articles/synthsecrets.htm">Synth Secrets, Part 7: Envelopes, Gates & Triggers</a></li>
<li><a href="http://en.wikipedia.org/wiki/Synthesizer#ADSR_envelope">Wikipedia article on ADSR envelopes</a></li>
</ul>
Playing notes with the Web Audio API Part 2 - Polyphonic Synthesis2013-06-10T00:00:00+00:00https://chrislowis.co.uk/2013/06/10/playing-multiple-notes-web-audio-api
<p>In <a href="/2013/06/05/playing-notes-web-audio-api.html">my last post</a>, I talked about playing notes with the Web Audio API
using techniques inspired by monophonic synthesizers, such as the
Minimoog. The advantage of this approach was in its simplicity and
that by using a single oscillator as our source of sound we could
easily slide between notes when playing.</p>
<p>The principle disadvantage was, of course, that with a single
oscillator as our source of sound we could only play one note at a
time. Fine for throaty, analogue sounding synth lines, or a flute
simulator perhaps, but not so useful for creating lush string pads or
simulating a piano or Hammond organ.</p>
<h2 id="what-is-polyphony">What is polyphony?</h2>
<p>Simply put, polyphony is the simultaneous playing of independent
melodies. A string quartet for example has 4 instruments playing at
the same time. In synthesis, polyphony describes the ability of a
single instrument to play multiple <em>notes</em> simultaneously, allowing
you to play a chord or an arpeggio with overlapping notes.</p>
<h2 id="a-polyphonic-synthesizer">A polyphonic synthesizer</h2>
<p>Early synthesizer pioneers were incredibly creative at getting around
the limitations and reliability of analogue circuits to produce a
range of polyphonic, and <em>nearly</em> polyphonic synths.
<a href="http://www.soundonsound.com/sos/dec00/articles/synthsec.asp">This great ‘Synth Secrets’ article on polyphony</a>
from Sound on Sound magazine gives a lot of historical detail of the
development of various approaches. For our purposes though, let’s
consider the synthesizers of Oberheim.</p>
<p>This is an Oberheim SEM monosynth module.</p>
<p><a href="http://www.vintagesynth.com/oberheim/sem.php"><img src="/images/oberheim_sem_lg.jpg" /></a></p>
<p>The control panel looks very much like the
<a href="/2013/06/05/playing-notes-web-audio-api.html">simple “monosynth”</a> we
talked about last time - an Oscillator (VCO) or two with something to
control the amplitude, marked here ENV, for envelope, but also known
as a VCA (Voltage Controlled Amplifier).</p>
<p>If you bought one of these modules and connected it to a standalone
keyboard, you had a monosynth. But what if you could attach a module
to each individual key on the keyboard? Then you’d be able to play
multiple notes simultaneously: polyphony!</p>
<p>In practice, at the time, a synthesizer with so many modules would
have been very expensive and unreliable. What Oberheim chose to do
instead was to build synthesizers with a smaller number of these
modules, and then use a bit of clever circuitry called a
<a href="http://www.soundonsound.com/sos/jan01/articles/synthsec.asp">Voice Allocation Unit</a>
to allocate a key press to one of the individual modules - whichever
one was “free” at the time. The Oberheim
<a href="http://www.vintagesynth.com/oberheim/2voice.php">2-Voice</a> had two SEM
modules, the
<a href="http://www.vintagesynth.com/oberheim/4voice.php">4-Voice</a> four and
the <a href="http://www.vintagesynth.com/oberheim/8voice.php">8-Voice</a>, well,
you get the idea.</p>
<h1 id="polyphony-with-the-web-audio-api">Polyphony with the Web Audio API</h1>
<p>Nowadays with digital technology and the Web Audio API in particular,
we are not limited to a fixed number of voices for cost or reliability
reasons. Oscillators and Amplifiers are cheap to create, so we can in
effect create a new Voice each time a key is pressed. We’ll use that
example to convert our synth from the previous article into a
polyphonic one.</p>
<p>To begin with let’s take the VCO and VCA from the previous article and wrap them in
a “class” called Voice</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="kd">var</span> <span class="nx">Voice</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">context</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">Voice</span><span class="p">(</span><span class="nx">frequency</span><span class="p">){</span>
<span class="k">this</span><span class="p">.</span><span class="nx">frequency</span> <span class="o">=</span> <span class="nx">frequency</span><span class="p">;</span>
<span class="p">};</span>
<span class="nx">Voice</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">start</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="cm">/* VCO */</span>
<span class="kd">var</span> <span class="nx">vco</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">createOscillator</span><span class="p">();</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">type</span> <span class="o">=</span> <span class="nx">vco</span><span class="p">.</span><span class="nx">SINE</span><span class="p">;</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">frequency</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">frequency</span><span class="p">;</span>
<span class="cm">/* VCA */</span>
<span class="kd">var</span> <span class="nx">vca</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">createGain</span><span class="p">();</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="mf">0.3</span><span class="p">;</span>
<span class="cm">/* connections */</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vca</span><span class="p">);</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">destination</span><span class="p">);</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">start</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="p">};</span>
<span class="k">return</span> <span class="nx">Voice</span><span class="p">;</span>
<span class="p">})(</span><span class="nx">context</span><span class="p">);</span></code></pre></figure>
<p>This class takes a frequency parameter in its constructor and when the
“start” method is called, constructs the =OscillatorNode= and
<code class="language-plaintext highlighter-rouge">GainNode</code> from the monosynth example and connects them to the
<code class="language-plaintext highlighter-rouge">destination</code>. If we modify the <code class="language-plaintext highlighter-rouge">keyDown</code> function to create a new
Voice on each keypress, we have basic polyphony.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">keyboard</span><span class="p">.</span><span class="nx">keyDown</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">frequency</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">voice</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Voice</span><span class="p">(</span><span class="nx">frequency</span><span class="p">);</span>
<span class="nx">voice</span><span class="p">.</span><span class="nx">start</span><span class="p">()</span>
<span class="p">});</span></code></pre></figure>
<p>If you
<a href="/demos/polyphonic_synthesis/demo1/synth.html">try this example</a>
you’ll notice straight away that we can now play multiple simultaneous
notes on the keyboard. Now we just need to be able to stop them! This
isn’t quite as straightforward as before as we don’t have a single VCA
we can set to zero. Just as with the Oberheim SEM modules that are our
inspiration, for each note we now have to control the parameters for
that individual Voice.</p>
<p>We start by adding a <code class="language-plaintext highlighter-rouge">stop</code> method to <code class="language-plaintext highlighter-rouge">Voice</code>. This simply needs to
<code class="language-plaintext highlighter-rouge">stop()</code> all of the active oscillators for this Voice. To achieve this, we
need to keep track of the oscillators we add to the graph</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">Voice</span> <span class="o">=</span> <span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">context</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">function</span> <span class="nx">Voice</span><span class="p">(</span><span class="nx">frequency</span><span class="p">){</span>
<span class="k">this</span><span class="p">.</span><span class="nx">frequency</span> <span class="o">=</span> <span class="nx">frequency</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nx">oscillators</span> <span class="o">=</span> <span class="p">[];</span>
<span class="p">};</span>
<span class="nx">Voice</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">start</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="cm">/* ... */</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">start</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="cm">/* Keep track of the oscillators used */</span>
<span class="k">this</span><span class="p">.</span><span class="nx">oscillators</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">vco</span><span class="p">);</span>
<span class="p">};</span>
<span class="cm">/* ... */</span></code></pre></figure>
<p>And then to stop the Voice we can call <code class="language-plaintext highlighter-rouge">stop()</code> on each of the
oscillators</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="cm">/* ... */</span>
<span class="nx">Voice</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">stop</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="nx">oscillators</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">oscillator</span><span class="p">,</span> <span class="nx">_</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">oscillator</span><span class="p">.</span><span class="nx">stop</span><span class="p">();</span>
<span class="p">});</span>
<span class="p">};</span>
<span class="cm">/* ... */</span></code></pre></figure>
<p>Then, if we keep track of which Voice is associated with each key
pressed, we can call <code class="language-plaintext highlighter-rouge">stop()</code> when the key is released. This is
straightforward if we store each Voice in an object</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"> <span class="nx">active_voices</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">keyboard</span><span class="p">.</span><span class="nx">keyDown</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">note</span><span class="p">,</span> <span class="nx">frequency</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">voice</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Voice</span><span class="p">(</span><span class="nx">frequency</span><span class="p">);</span>
<span class="nx">active_voices</span><span class="p">[</span><span class="nx">note</span><span class="p">]</span> <span class="o">=</span> <span class="nx">voice</span><span class="p">;</span>
<span class="nx">voice</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span>
<span class="p">});</span>
<span class="nx">keyboard</span><span class="p">.</span><span class="nx">keyUp</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">note</span><span class="p">,</span> <span class="nx">_</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">active_voices</span><span class="p">[</span><span class="nx">note</span><span class="p">].</span><span class="nx">stop</span><span class="p">();</span>
<span class="k">delete</span> <span class="nx">active_voices</span><span class="p">[</span><span class="nx">note</span><span class="p">];</span>
<span class="p">});</span></code></pre></figure>
<p>So there we have it, a very simple
<a href="/demos/polyphonic_synthesis/demo2/synth.html">polyphonic synthesizer</a>.
You’ll notice some clicking when the keys are pressed and also some
distortion when too many notes are played. I’ll address these issues,
and look at how to synthesise more exciting sounds in later posts.</p>
<!-- LocalWords: API AudioContext createOscillator endhighlight VCO
-->
<!-- LocalWords: AudioBufferSourceNode synth Moog's Minimoog VCA
-->
<!-- LocalWords: monosynth DOM qwertyHancock vco vca createGain src
-->
<!-- LocalWords: keyDown keyUp startNote synthesiser isEmpty webkit
-->
<!-- LocalWords: monkeypatch webkitAudioContext contentfor synths
-->
<!-- LocalWords: endcontentfor Oberheim SEM href img ENV GainNode
-->
<!-- LocalWords: OscillatorNode keypress
-->
Playing notes with the Web Audio API Part 1 - Monophonic Synthesis2013-06-05T00:00:00+00:00https://chrislowis.co.uk/2013/06/05/playing-notes-web-audio-api
<p>The
<a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html">Web Audio API</a>
turns your browser into a powerful synthesizer. One question I’ve been
asked about the Web Audio API is how to play single, one-shot sounds
using the oscillators, in other words how to play notes or tunes. I’ll
show one approach in this post and along the way we’ll look at how
some early analogue monophonic synths worked.</p>
<p>If you’re new to the Web Audio API there’s a
<a href="http://www.html5rocks.com/en/tutorials/webaudio/intro/">good introduction over on HTML5 Rocks</a>.
The first couple of sections should be all you need to follow this
tutorial.</p>
<h2 id="playing-a-sound">Playing a sound</h2>
<p>We start by creating some sound. This could be considered the “hello
world” of Web Audio applications - we’ll generate music’s purest tone,
the <a href="http://en.wikipedia.org/wiki/Sine_wave">sine wave</a>, and make it
play.</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">context</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">AudioContext</span><span class="p">;</span>
<span class="nx">oscillator</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">createOscillator</span><span class="p">();</span>
<span class="nx">oscillator</span><span class="p">.</span><span class="nx">frequency</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="mi">200</span><span class="p">;</span>
<span class="nx">oscillator</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">destination</span><span class="p">);</span>
<span class="nx">oscillator</span><span class="p">.</span><span class="nx">start</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></code></pre></figure>
<p>If you open a javascript console on this page and enter the code
above, assuming your browser supports the Web Audio API<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>, you
should hear a tone at 200Hz. We’ve created an <code class="language-plaintext highlighter-rouge">AudioContext</code>, added an
oscillator and told it to start playing immediately. If you don’t set
the frequency the default is 440Hz, or an A above middle C. Why 440Hz?
<a href="http://en.wikipedia.org/wiki/History_of_pitch_standards_in_Western_music#History_of_pitch_standards_in_Western_music">I thought you’d never ask</a>!</p>
<p>We can stop the tone with</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">oscillator</span><span class="p">.</span><span class="nx">stop</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></code></pre></figure>
<p>At this point, you might be tempted to start the oscillator again</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nx">oscillator</span><span class="p">.</span><span class="nx">start</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span></code></pre></figure>
<p>but you’ll find, if you try, that this doesn’t work. For performance
reasons the Web Audio API is designed so that oscillator, and other
nodes such as AudioBufferSourceNode can only be “used” once. So if we
want to play a sequence of notes, or attach notes to the keys of a
virtual keyboard, how do we proceed? To answer this question, let’s
consider how analogue synth designers tackled the problem.</p>
<h2 id="monophonic-synthesis">Monophonic Synthesis</h2>
<p>One classic monophonic synthesizer is Robert Moog’s “Minimoog”. This
was a <em>monophonic synthesizer</em> - that is, it could only play a single
note at a time. Here’s the control panel from the Minimoog:</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/25/Minimoog_panel.jpg/800px-Minimoog_panel.jpg" alt="Minimoog Control Panel" title="Minimoog control panel" /></p>
<p>The sections we are interested in here are the “oscillator bank”<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup> and
the “loudness contour” controls, also known as the VCO (Voltage
Controlled Oscillator) and VCA (Voltage Controlled Amplifier)
respectively<sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup>.</p>
<p>The VCO, as it’s name suggests, is an oscillator whose frequency can
be set by sending a specific voltage - typically from the keyboard of
the instrument. In our code above, this is equivalent to the
<code class="language-plaintext highlighter-rouge">oscillator.frequency.value =</code>.</p>
<p>The VCA is an amplifier which varies its gain depending on a supplied
voltage. For simplicity, let’s assume that when it receives a positive
voltage it sets its gain to 1, and when it receives a negative voltage
it sets its gain to zero.</p>
<p>When a musician presses a key on the keyboard, two things happen.
Firstly a voltage is sent to the VCO to set its frequency. Secondly,
the same key press sets the gain of the VCA. The oscillator is
connected to the amplifier so pressing the key makes a sound. When
the key is released, the VCA is set to zero and the sound stops.</p>
<p>Armed with this knowledge, we can simulate this using the Web Audio
API.</p>
<h2 id="a-simple-web-audio-monosynth">A simple Web Audio monosynth</h2>
<p>To recreate the simple Minimoog described above we need two nodes from
the Web Audio API, connected together like so:</p>
<p><img src="/images/monosynth.png" alt="Monosynth block diagram" title="Block diagram for monosynth" /></p>
<p>We also need a mechanism to send messages to the nodes using
key-presses. We’ll use Stuart Memo’s very useful
<a href="http://stuartmemo.com/qwerty-hancock/">Qwerty Hancock</a> library for
this. It turns a given DOM element into an interactive vector
keyboard!</p>
<p>A little bit of glue code, and we have</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">keyboard</span> <span class="o">=</span> <span class="nx">qwertyHancock</span><span class="p">({</span><span class="na">id</span><span class="p">:</span> <span class="dl">'</span><span class="s1">keyboard</span><span class="dl">'</span><span class="p">});</span>
<span class="kd">var</span> <span class="nx">context</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">AudioContext</span><span class="p">();</span>
<span class="cm">/* VCO */</span>
<span class="kd">var</span> <span class="nx">vco</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">createOscillator</span><span class="p">();</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">type</span> <span class="o">=</span> <span class="nx">vco</span><span class="p">.</span><span class="nx">SINE</span><span class="p">;</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">frequency</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">frequency</span><span class="p">;</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">start</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>
<span class="cm">/* VCA */</span>
<span class="kd">var</span> <span class="nx">vca</span> <span class="o">=</span> <span class="nx">context</span><span class="p">.</span><span class="nx">createGain</span><span class="p">();</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="cm">/* Connections */</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">vca</span><span class="p">);</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="nx">context</span><span class="p">.</span><span class="nx">destination</span><span class="p">);</span>
<span class="nx">keyboard</span><span class="p">.</span><span class="nx">keyDown</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">frequency</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">frequency</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">frequency</span><span class="p">;</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">keyboard</span><span class="p">.</span><span class="nx">keyUp</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">_</span><span class="p">,</span> <span class="nx">_</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">});</span></code></pre></figure>
<script type="text/javascript">
$(function() {
var keyboard = qwertyHancock({id: 'keyboard'});
var context = new AudioContext();
/* VCO */
var vco = context.createOscillator();
vco.type = vco.SINE;
vco.frequency.value = this.frequency;
vco.start(0);
/* VCA */
var vca = context.createGain();
vca.gain.value = 0;
/* Connections */
vco.connect(vca);
vca.connect(context.destination);
keyboard.keyDown(function (_, frequency) {
vco.frequency.value = frequency;
vca.gain.value = 1;
});
keyboard.keyUp(function (_, _) {
vca.gain.value = 0;
});
});
</script>
<p>When a key on our virtual keyboard is pressed, the keyDown event is
fired. We set the frequency of the oscillator and turn the gain of the
amplifier up to 1. When the key is lifted we set the gain to zero.</p>
<p>Here’s the simple synthesiser in action:</p>
<div id="keyboard"></div>
<p>There’s a few tweaks we should make here, as I’ve deliberately kept the
code very simple. For example, if you press two keys at the same time
and then release one, the gain will be set to zero. This can be
avoided by keeping track of the keys that are pressed using an object</p>
<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">isEmpty</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">keys</span><span class="p">(</span><span class="nx">obj</span><span class="p">).</span><span class="nx">length</span> <span class="o">===</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="nx">depressed_keys</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">keyboard</span><span class="p">.</span><span class="nx">keyDown</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">note</span><span class="p">,</span> <span class="nx">frequency</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">vco</span><span class="p">.</span><span class="nx">frequency</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">frequency</span><span class="p">;</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="nx">depressed_keys</span><span class="p">[</span><span class="nx">note</span><span class="p">]</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">keyboard</span><span class="p">.</span><span class="nx">keyUp</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">note</span><span class="p">,</span> <span class="nx">_</span><span class="p">)</span> <span class="p">{</span>
<span class="k">delete</span> <span class="nx">depressed_keys</span><span class="p">[</span><span class="nx">note</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">isEmpty</span><span class="p">(</span><span class="nx">depressed_keys</span><span class="p">))</span> <span class="p">{</span>
<span class="nx">vca</span><span class="p">.</span><span class="nx">gain</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span></code></pre></figure>
<p>The completed code is
<a href="https://gist.github.com/chrislo/5717561">in this gist</a>.</p>
<p>The other thing you might hear when playing with this demo is the
“slide” between each note. When setting the value of some parameters
directly in the API, as we do here with <code class="language-plaintext highlighter-rouge">frequency</code>, the spec
recommends that changes are made smoothly to avoid clicks or glitches.
We’ll see how to control this effect in a future post when we look at
parameter automation and envelopes.</p>
<h2 id="next">Next</h2>
<p>We’ve seen how to play notes using the Web Audio API using code
influenced by the design of classic monophonic synthesizers. Next time
we’ll have a look at techniques for playing multiple notes at the same
time: polyphonic synthesis.</p>
<h2 id="footnotes">Footnotes</h2>
<p><a href="https://github.com/cwilso/AudioContext-MonkeyPatch">AudioContext monkeypatch</a>
on this page so that I can use <code class="language-plaintext highlighter-rouge">AudioContext</code> instead of the
vendor-prefixed <code class="language-plaintext highlighter-rouge">webkitAudioContext</code> on Chrome. The code examples here
match the Web Audio spec and can work in non-webkit browsers, such as
Firefox, as well. If you’re trying these examples in a page that
doesn’t have the patch loaded, you’ll need to replace <code class="language-plaintext highlighter-rouge">AudioContext</code>
with <code class="language-plaintext highlighter-rouge">webkitAudioContext</code>.</p>
<p>oscillators - the Minimoog could mix together the sound of three
oscillators, but only to combine them into a single “note” - they
couldn’t be triggered by independent keys on the keyboard.</p>
<!-- LocalWords: API AudioContext createOscillator endhighlight VCO
-->
<!-- LocalWords: AudioBufferSourceNode synth Moog's Minimoog VCA
-->
<!-- LocalWords: monosynth DOM qwertyHancock vco vca createGain
-->
<!-- LocalWords: keyDown keyUp startNote synthesiser isEmpty webkit
-->
<!-- LocalWords: monkeypatch webkitAudioContext
-->
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>I’m using Chris Wilson’s <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p>If you look closely at the oscillator bank you’ll see three <a href="#fnref:2" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p>We’ll come back to some of the other sections in future posts. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Using YouTube videos in HTML5 presentations2013-04-19T00:00:00+00:00https://chrislowis.co.uk/2013/04/19/youtube-html5-presentations<p>I recently gave a presentation at
<a href="http://devslovebacon.com/conferences/bacon-2013/talks">Bacon 2013</a>.
My presentation involved demoing some features of the Web Audio API,
so I built it using <a href="http://imakewebthings.com/deck.js/">deck.js</a> so
that I could present using Google Chrome.</p>
<p>I wanted to include some YouTube videos inside <code class="language-plaintext highlighter-rouge"><video></code> elements in my
deck. Here’s my process for creating those videos on OS X Mountain
Lion using command line tools.</p>
<h3 id="downloading-youtube-videos">Downloading YouTube videos</h3>
<p>To download the videos there’s a simple command-line tool called
<a href="http://rg3.github.io/youtube-dl/">youtube-dl</a> (install with <code class="language-plaintext highlighter-rouge">brew
install youtube-dl</code> if you’re using homebrew). To download a youtube
video you just pass in the url</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ youtube-dl http://www.youtube.com/watch?v=dQw4w9WgXcQ
</code></pre></div></div>
<h3 id="converting-flv-to-h264-with-aac-audio">Converting FLV to h264 with AAC audio</h3>
<p>The best combination of video and audio codecs I found for displaying
my slide deck in Google Chrome was h264 with AAC audio.
<a href="http://www.ffmpeg.org/">FFmpeg</a> (install with <code class="language-plaintext highlighter-rouge">brew install ffmpeg</code>)
handles the conversion from FLV</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ffmpeg -i input_file.flv -c:a aac -strict -2 -b:a 128k -c:v libx264 -profile:v baseline output_file.mp4
</code></pre></div></div>
<h3 id="extracting-a-segment-of-the-video">Extracting a segment of the video</h3>
<p>If you only want a small part of a video file you can use FFmpeg to
extract a segment</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ ffmpeg -i input_file.mp4 -acodec copy -vcodec copy -ss 00:04:00 -t 00:00:20 output_file.mp4
</code></pre></div></div>
<p>Where the first time stamp is the start of the segment and the <code class="language-plaintext highlighter-rouge">-t</code>
option is the duration (in <code class="language-plaintext highlighter-rouge">HH:MM:SS</code> format).</p>
<p>The output file is now suitable for use in a <code class="language-plaintext highlighter-rouge"><video></code> tag.</p>
Ruby Manor 20112011-11-20T00:00:00+00:00https://chrislowis.co.uk/2011/11/20/ruby-manor-2011<p>
The third <a href="http://rubymanor.org/3/">Ruby Manor</a> conference happened in London a few weeks ago. I
thought I'd give my reflections on the event in the old-fashioned blog
post style.
</p>
<div id="outline-container-1" class="outline-3">
<h3 id="sec-1">Philosophy </h3>
<div class="outline-text-3" id="text-1">
<p>
Ruby Manor is run according to a certain philosophy. Some of it was
<a href="https://groups.google.com/d/topic/ruby-manor/zxepNy-VLHE/discussion">explained upfront</a> by James, Murray and Tom (this year's organising
team), and some of it is implicit - drawn largely from the way
previous Manors and the monthly LRUG meetings are run. Ruby Manor <a href="http://interblah.net/ruby-manor-is-not-an-unconference">is not an unconference</a>, it doesn't have keynote speakers, swag, dinners
or t-shirts. It costs less than £15. The speakers and topics are
chosen by the attendees and <a href="http://vestibule.rubymanor.org/champs">contribution to the process is encouraged</a>.
</p>
</div>
</div>
<div id="outline-container-2" class="outline-3">
<h3 id="sec-2">Organisation </h3>
<div class="outline-text-3" id="text-2">
<p>
Before last year's conference, speakers proposed their talks on a
mailing list. We asked questions, and the organising team selected the
final list of presentations. This year, this system was enhanced and
formalised as <a href="http:vestibule.rubymanor.org">Vestibule</a> - a simple web application designed to
encourage and facilitate engagement with the organisation of the
conference. The most interesting section is the short <a href="http://vestibule.rubymanor.org/motivation">motivations</a>
given by the attendees for participating. Quite soon after the site
launched a number of talks were <a href="http://vestibule.rubymanor.org/proposals">proposed</a>. As an example, consider the
(ultimately unsuccessful) talk <a href="http://vestibule.rubymanor.org/proposals/16">I proposed</a>. I gave an outline of the
talk I wanted to give and asked for feedback and suggestions. I was
encouraged by the level of support and worked further to add more
information to the proposal based on the feedback. About 3 weeks
before the event, the proposals were open to the vote and whittled
down to a final <a href="http://vestibule.rubymanor.org/selections">selection</a>.
</p>
<p>
Vestibule used a points-based "karma" system to <a href="http://vestibule.rubymanor.org/champs">rank the participants</a>
on their level of engagement. I'm not entirely sure what impact the
score had, but I believe tickets were made available in batches to
those with the highest scores first. James Adam <a href="http://interblah.net/early-bird-tickets">talks more about the philosophy of conference ticketing</a> on his blog.
</p>
<p>
I found the Vestibule system quite addictive - it was something I
found myself checking each day, especially as new features were rolled
out as the process went along. I'd be interested to see some analysis
of the level of engagement too - did it demonstrate a <a href="http://en.wikipedia.org/wiki/Pareto_principle">typical split</a> in
engagement between active and passive, or did the presence of rewards
encourage increased engagement?
</p>
</div>
</div>
<div id="outline-container-3" class="outline-3">
<h3 id="sec-3">Venue </h3>
<div class="outline-text-3" id="text-3">
<p>
The venue this year was the University of Westminster building on New
Cavendish street. The lecture-theatre used had a raked seating
arrangement with a large projector which meant that everyone got a
good view of the talks. The venue was close to a large number of
places to grab lunch and coffee during the breaks. I'm not sure what
the WIFI was like as I didn't take a laptop, but I don't think people
had too many problems. There was very little space at the back of the
room which meant that you had to be in your seats in time for the
talks and were essentially captive for the duration of the sessions,
but the individual talks were of equal length which made shuffling
around easy.
</p>
</div>
</div>
<div id="outline-container-4" class="outline-3">
<h3 id="sec-4">Lunch </h3>
<div class="outline-text-3" id="text-4">
<img src="/images/lunch.jpg" width="600" alt="Ruby Manor Machine Learning Lunch"></img>
<p>
I experimented with organising "Birds of a Feather" lunches this
year. The idea is to have lunch with people who share a common
interest, and is organised by having people sign up to lunch-time
groups on the day. Although I got a good-sized group of people
together for a "machine-learning" themed lunch, I don't think many
other topics formed. In retrospect I think it would have been good to
organise lunch "champions" in advance to lead a group around a
particular topic. Also as the conference was Ruby focussed, I guess
people found it easy to find others with at least Ruby as a common
interest.
</p>
</div>
</div>
<div id="outline-container-5" class="outline-3">
<h3 id="sec-5">The Talks </h3>
<div class="outline-text-3" id="text-5">
<p>
<a href="http://lanyrd.com/2011/ru3y-manor/schedule/">The talks</a> were universally of a very high standard. There wasn't a
single one that I didn't get something out of, and a couple were among
the best I've ever seen at any conference. Keep an eye on the <a href="http://rubymanor.org">Ruby Manor</a> website for the videos when they're released. For me the real
standouts were:
</p>
</div>
<div id="outline-container-5-1" class="outline-4">
<h4 id="sec-5-1">Programming with Nothing by Tom Stuart </h4>
<div class="outline-text-4" id="text-5-1">
<p>
<a href="http://experthuman.com/">Tom</a> introduced us to the <a href="http://en.wikipedia.org/wiki/Lambda_calculus">Lambda Calculus</a> by considering what is
possible in Ruby if we strip the language right back to basics. He
implemented a solution to the FizzBuzz problem using just Ruby's
<code>Proc</code> object and the methods <code>Proc.new</code> and <code>Proc#call</code>. What started
out as a curiosity turned into a discourse on the nature of
computation and the power of functional programming. It ended with a
roar of approval when the final code was run! The slides were superb,
the delivery style was quirky enough to be at home at a Ruby
conference but had a real depth - there's no surprise to learn Tom
used to lecture Computer Science topics at Cambridge! A real treat.
</p>
</div>
</div>
<div id="outline-container-5-2" class="outline-4">
<h4 id="sec-5-2">A Random Walk by Ben Griffiths </h4>
<div class="outline-text-4" id="text-5-2">
<p>
I really enjoyed <a href="http://www.techbelly.com/">Ben Griffiths</a>'s talk on Randomness. He gave a
grab-bag selection of slides on such topics as Monte Carlo analysis,
Simulated Annealing and a thought experiment on what it would be like
to have a CI system that deployed to your production environment
randomly. Ben organised his talk as one "idea" per slide, illustrated
with occasional Ruby code, and then randomised the order just to keep
himself and us on our toes. A fun idea and one that would have tripped
up most speakers, I think. Not Ben though, he's a fantastic speaker
and one I always enjoy seeing.
</p>
</div>
</div>
<div id="outline-container-5-3" class="outline-4">
<h4 id="sec-5-3">The Joy of Text by Sean O'Halpin </h4>
<div class="outline-text-4" id="text-5-3">
<p>
I was looking forward to this talk - sitting next to Sean <a href="http://bbc.co.uk/rd">at work</a>,
I've seen it come together slowly off the back of a lot of
research. Sean talked about text encodings, his <a href="http://github.com/seanohalpin/ffi-ncurses">ffi-ncurses</a> library
and the history of terminal interfaces. He attempted to explain some
of the problems we programmers face everyday when we work with text
and text-based interfaces. It was a talk which covered a lot of
ground, and hid a lot of depth and knowledge of the subject under a
veneer of amusing anecdotes and useful tips. I hope the content of the
talk makes it out in a longer form (a series of blog articles would be
great, if you're reading, Sean!) as there's a lot of very useful
content for us all to learn in there.
</p>
</div>
</div>
</div>
<div id="outline-container-6" class="outline-3">
<h3 id="sec-6">Conclusion </h3>
<div class="outline-text-3" id="text-6">
<p>
I hope this post has given you some feel for how great again the Ruby Manor
conference was this year, and has encouraged you to seek out the
slides and videos when they appear (it looks like the <a href="http://lanyrd.com/2011/ru3y-manor/schedule/">lanyrd page</a>
might be a good place to find everything together). It was a
conference as much about the people and the community as it was the
talks and the programming language. An experiment in self-organisation
which showed how difficult it is to get people to step out of the
comfort zone and contribute to the shaping of their community, but
also what can be achieved when a few of them do so.
</p>
<p>
Thanks to Murray, James, Tom and all the champs who contributed to
this year's event. Well done all!
</p></div>
</div>
Great radio interviews2011-05-18T00:00:00+00:00https://chrislowis.co.uk/2011/05/18/great-radio-podcast-interviews<p>
I'm a big fan of radio, and one format I particularly enjoy is the
long-form interview. Here are some of the interviews I've really
enjoyed in recent months, available as podcasts or downloads, and some
reasons why you might enjoy them too.
</p>
<div id="outline-container-1" class="outline-3">
<h3 id="sec-1"><a href="http://5by5.tv/pipeline/49">Philip Elmer-Dewitt interviewed by Dan Benjamin on The Pipeline</a> </h3>
<div class="outline-text-3" id="text-1">
<p>
I'm a fan of Dan Benjamin's 5by5 radio network. I'm less keen on the
wide ranging discussion-show formats but when he conducts one-on-one
interviews with people he obviously respects his interviewing style
really brings out the best in his guests.
</p>
<a href="https://www.flickr.com/photos/imh/3297961043/" title="Radio Daze by Ian Hayhurst, on Flickr"><img src="https://farm4.static.flickr.com/3317/3297961043_1ab2a0f94b_m.jpg" width="240" height="160" alt="Radio Daze"></a>
<p>
In this interview with <a href="http://en.wikipedia.org/wiki/Philip_Elmer-DeWitt">Philip Elmer-Dewitt</a> they discuss the long
career of this prolific ex-Time writer. Elmer-Dewitt is remarkably
self-effacing, but I was inspired by the tale of his transition from
technical writer/programmer to eventually Science Editor of Time
magazine. The day-to-day accounts of his working life and tales of the discipline
he's cultivated are truly fascinating. There's a lot of wisdom in this
interview!
</p>
</div>
</div>
<div id="outline-container-2" class="outline-3">
<h3 id="sec-2"><a href="http://www.bbc.co.uk/radio4/features/desert-island-discs/castaway/d8176a1c#b00p068y">Morrissey interviewed by Kirsty Young on Desert Island Discs</a> </h3>
<div class="outline-text-3" id="text-2">
<p>
The recently-launched Desert Island Discs archive website has made
hundreds of interviews from the long-running Radio 4 show's archive
available to "download and keep". Throwing in some of my favourite
band's names into the search box led to this gem from 2009.
</p>
<p>
Kirsty Young asks pointed, revealing questions often as simple
statements to be refuted ("You're not traditionally romantic") but
Morrissey is more willing to engage than usual ("I think I am"). He
talks freely about his childhood, the cult of celebrity, why four
years of the Smiths over-shadows the rest of his career and along the
way gets chance to play some great records from The Ramones, Iggy &
The Stooges and The Velvet Underground.
</p>
</div>
</div>
<div id="outline-container-3" class="outline-3">
<h3 id="sec-3"><a href="http://www.maximumfun.org/sound-young-america/ian-mackaye-fugazi-minor-threat-and-evens-interview-sound-young-america ">Ian Mackaye interviewed by Jesse Thorn on the Sound of Young America</a></h3>
<div class="outline-text-3" id="text-3">
<p>
I'd not heard of this show before Dan Benjamin interviewed Jesse Thorn
on The Pipeline. It appears to be very popular across the Atlantic,
being syndicated widely via NPR. As I clicked through the
back-catalogue of shows I came across this interview with Ian Mackaye,
guitarist in one of my favourite bands, Fugazi, and head of Dischord
records.
</p>
<p>
In the interview Mackaye talks more about his personal philosophy
rather than the music - what it's like to run a business with
principles, how it feels to inspire an entire youth movement and the
passion he has for the Washington DC community. Mackaye is a great
talker with conviction and experience; he rarely gives interviews so
this was a real treat.
</p>
</div>
</div>
<div id="outline-container-4" class="outline-3">
<h3 id="sec-4"><a href="http://twit.tv/floss136">Carsten Dominik interviewed by Randal Schwartz on FLOSS Weekly</a> </h3>
<div class="outline-text-3" id="text-4">
<p>
I'm not quite the regular listener I once was to FLOSS weekly. Randal
Schwartz is a knowledgeable and competent interviewer, but he doesn't
have the easy-on-the ears style of former co-host Leo Laport. I still
tune in when the topic or guest looks interesting though, and did so
for this interview with Carsten Dominik, creator of <code>org-mode</code> for
Emacs. I spend a good portion of my working life inside the venerable
old editor, and organise my thoughts and projects using org-mode.
</p>
<p>
Org is on the surface a simple, useful piece of software, as Dominik
makes clear its inception was certainly a case of scratching a
personal itch, but its the community that has driven the development
of its vast array of other features. Listening to Dominik's passion and
theories on community management in open source software was very
motivating, and the fact that he developed the software alongside a
busy scientific life must say something about the power of org-mode as
a time management tool!
</p>
</div>
</div>
<div id="outline-container-5" class="outline-3">
<h3 id="sec-5"><a href="http://freakonomicsradio.com/growing-up-buffett.html">Peter Buffet interviewed by Stephen Dubner on Freakonomics Radio</a> </h3>
<div class="outline-text-3" id="text-5">
<p>
It's difficult to find radio programming that is not scared of numbers
or the statistics behind news stories. Stephen Dubner has made a
career out of asking the kind of questions that others seldom do, or
at the very least presenting the research that others are doing to his
audience in a compelling way. He's recently started a radio show too,
and some of the episodes are interesting interviews. In this episode
he talks to Peter Buffet - composer, musician and youngest son of
Warren Buffet.
</p>
<p>
As you can expect, the topic of his famous father is never far away,
but it was the discussion about his early family life that I enjoyed,
and the conviction Peter Buffet had to follow his own path. It was an
enjoyable interview, Dubner let his guest speak without the clever
verbal tricks of some of his other episodes.
</p></div>
</div>
</div>
</body>
</html>
Joining BBC Research and Development2011-04-06T00:00:00+00:00https://chrislowis.co.uk/2011/04/06/joining-bbc-rd<p>
A couple of months ago I joined the <a href="http://www.bbc.co.uk/blogs/researchanddevelopment/prototyping/">Prototyping team</a> in <a href="http://bbc.co.uk/rd">BBC R&D</a>. It's
a great chance to work on cutting edge technologies for broadcast and
the web. And a great place to work with some really smart people.
</p>
<p>
The first thing I've been working on is the fledgling <a href="http://groups.google.com/group/radiotag-developers">RadioTAG</a>
specification, part of the <a href="http://radiodns.org">RadioDNS</a> umbrella of technologies. We're
looking at adding a button to radios or other radio-receiving devices
that will allow a listener to register their interest in a
broadcast. Maybe you've heard a track you'd like to listen to again,
or you have to leave the house during the Today programme and want to
carry on listening where you left off when you reach the
office. Interesting stuff.
</p>
<p>
As my colleague <a href="http://whomwah.com">Duncan</a> <a href="http://whomwah.com/2011/01/07/2011-is-already-turning-out-to-be-amazing/">said</a>, we should all try to blog a little more
this year. I've <a href="http://www.bbc.co.uk/blogs/researchanddevelopment/2011/02/prototyping-weeknotes-51-25021.shtml">made</a> a <a href="http://www.bbc.co.uk/blogs/researchanddevelopment/2011/03/building-applications-on-large.shtml">start</a> over on our team's blog, but I'm going to
try and do so here too.
</p>
</div>
</body>
</html>
Using Rake and Git to find out what I did yesterday2010-11-24T00:00:00+00:00https://chrislowis.co.uk/2010/11/24/what-did-I-do-yesterday<p>
At our <a href="http://en.wikipedia.org/wiki/Stand-up_meeting">daily stand-ups</a> we have to answer the question "What did you do
yesterday". I wrote a little Rake task for the project I am working on
to help me answer:
</p>
<pre class="src src-ruby">desc <span style="color: #cc9393;">"List of all commits commited in the previous 24hours"</span>
task <span style="color: #dca3a3; font-weight: bold;">:yesterday</span> <span style="color: #f0dfaf; font-weight: bold;">do</span>
git_author = <span style="color: #dfdfbf; font-weight: bold;">ENV</span>[<span style="color: #cc9393;">'GIT_AUTHOR'</span>] || <span style="color: #cc9393;">"chris.lowis"</span>
cmd = <span style="color: #cc9393;">"git --no-pager log --after=\"1 day ago\" --pretty=oneline --author=</span><span style="color: #f0dfaf;">#{git_author}</span><span style="color: #cc9393;">"</span>
puts <span style="color: #cc9393;">"Commits in the last 24 hours for </span><span style="color: #f0dfaf;">#{git_author}</span><span style="color: #cc9393;">"</span>
system(cmd)
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
This simply shells out to git with the appropriate command line
arguments giving me a simple list:
</p>
<pre class="src src-sh">Commits<span style="color: #f0dfaf; font-weight: bold;"> in</span> the last 24 hours for chris.lowis
6663b95b5832sda91998c05fea7970fe44da13af Adding a great new feature
677b2e8092406b3c2031e8f065fg5672d90b8108 Fixing someone elses bug
</pre>
<p>
There's quite a lot of interesting options that you can pass to <code>git log</code>, <a href="http://www.kernel.org/pub/software/scm/git/docs/git-log.html">see the documentation</a> for a list of them.
</p>
</div>
</body>
</html>
Laptop Driven Development (with Emacs)2010-10-26T00:00:00+00:00https://chrislowis.co.uk/2010/10/26/laptop-driven-development-with-emacs<p>
I read an <a href="http://oinopa.com/2010/10/24/laptop-driven-development.html">interesting post</a> over on Bernerd Schaefers blog the other
day where he described his set up for coding when you only have a
small screen. He talks about methods for switching between VIM and a
terminal. If you're an emacs user, like me, it's more common to run
your terminal inside emacs.
</p>
<p>
I use the <a href="http://github.com/mxcl/homebrew">homebrew</a> package manager for OS X. One thing this allows me
to do is compile emacs from source with support for true full-screen
mode
</p>
<pre class="src src-sh">$ brew install emacs --cocoa
$ ln -s /usr/local/Cellar/emacs/23.2/Emacs.app /Applications/Emacs.app
</pre>
<p>
This gives me a recent emacs with a great, full-screen mode which I
activate by running <code>M-x ns-toggle-fullscreen</code>
</p>
<img src="/images/emacs-fullscreen.png" alt="Emacs fullscreen screenshot"></img>
<p>
That's my entire screen - no menubars, docks or other OS
paraphernalia. I can switch to a shell for running my unit tests by
typing <code>M-x eshell</code> (or for a standard bash shell <code>M-x shell</code>)
</p>
<img src="/images/emacs-terminal.png" alt="Emacs terminal screenshot"></img>
<p>
Since the usable area of the screen is quite large, thanks to the
fullscreen mode, I also have room to split the frame in two (<code>M-x split-window-vertically</code>) to see, for example, tests and
implementation at the same time
</p>
<img src="/images/emacs-split-2.png" alt="Emacs terminal screenshot"></img>
<p>
Or, occasionally, to pop open the shell buffer for quickly issuing a command
</p>
<img src="/images/emacs-split-3.png" alt="Emacs terminal screenshot"></img>
<p>
I find emacs to be a great environment to work in on a smaller
screen. You can see more of my emacs configuration on <a href="http://github.com/chrislo/emacs-config">github</a>.
</p>
</div>
</body>
</html>
Setting Http Proxy when using the selenium-webdriver gem and Firefox2010-07-14T00:00:00+00:00https://chrislowis.co.uk/2010/07/14/http-proxy-selenium-webdriver-and-firefox<p>
I've been using the <a href="http://rubygems.org/gems/selenium-webdriver">selenium-webdriver ruby gem</a> to do some automated
Cucumber testing of our application. Sadly, I'm sat behind a firewall
which requires an HTTP proxy to access the outside world. Using the
Chrome WebDriver bridge worked fine as it inherits the system
preferences on OS X. However with Firefox I had to perform some
trickery to get it to work:
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'rubygems'</span>
require <span style="color: #cc9393;">'selenium-webdriver'</span>
include <span style="color: #dfdfbf; font-weight: bold;">Selenium</span>
profile = <span style="color: #dfdfbf; font-weight: bold;">WebDriver</span>::<span style="color: #dfdfbf; font-weight: bold;">Firefox</span>::<span style="color: #dfdfbf; font-weight: bold;">Profile</span>.new
profile[<span style="color: #cc9393;">"network.proxy.type"</span>] = 1
profile[<span style="color: #cc9393;">"network.proxy.http"</span>] = <span style="color: #cc9393;">"www-cache.at.my.work.co.uk"</span>
profile[<span style="color: #cc9393;">"network.proxy.http_port"</span>] = 80
driver = <span style="color: #dfdfbf; font-weight: bold;">WebDriver</span>.for(<span style="color: #dca3a3; font-weight: bold;">:firefox</span>, <span style="color: #dca3a3; font-weight: bold;">:profile</span> => profile)
driver.navigate.to <span style="color: #cc9393;">"http://google.com"</span>
</pre>
<p>
The <code>[]=</code> method on <code>Profile</code> is setting the options that you find by
typing 'about:config' in the Firefox URL bar. Take a look there in a
working Firefox profile to see what variables you should set for your
webdriver-specific profile in the code above.
</p>
</div>
</body>
</html>
Growl notifications from Ruby on OS X2010-07-06T00:00:00+00:00https://chrislowis.co.uk/2010/07/06/growl-notifications-from-ruby-on-osx<p>
I wanted to generate <a href="http://growl.info/">Growl</a> notifications from a Ruby script on OSX and
had a bit of trouble getting it to work. To save you the same hassle,
here's the steps I took:
</p>
<div id="outline-container-1" class="outline-3">
<h3 id="sec-1">Install growl </h3>
<div class="outline-text-3" id="text-1">
<p>
Install <a href="http://growl.info/">Growl</a> if you haven't already. I used version 1.2.
</p>
</div>
</div>
<div id="outline-container-2" class="outline-3">
<h3 id="sec-2">Set some growl preferences </h3>
<div class="outline-text-3" id="text-2">
<p>
In the growl preference plane (find it under System Preferences)
</p><ul>
<li>
under network check "Listen for incoming notifications" and "Allow
remote application registration"
</li>
<li>
restart growl
</li>
</ul>
</div>
</div>
<div id="outline-container-3" class="outline-3">
<h3 id="sec-3">Install the ruby-growl gem </h3>
<div class="outline-text-3" id="text-3">
<p>
There's a couple of different ruby bindings for Growl but <a href="http://segment7.net/projects/ruby/growl/doc/">ruby-growl</a>
seemed to work for me
</p>
<pre class="src src-sh">gem install ruby-growl
</pre>
</div>
</div>
<div id="outline-container-4" class="outline-3">
<h3 id="sec-4">Send a growl notification from your Ruby script </h3>
<div class="outline-text-3" id="text-4">
<p>
You can now send notifications from Ruby
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'rubygems'</span>
require <span style="color: #cc9393;">'ruby-growl'</span>
g = <span style="color: #dfdfbf; font-weight: bold;">Growl</span>.new <span style="color: #cc9393;">"127.0.0.1"</span>, <span style="color: #cc9393;">"ruby-growl"</span>, [<span style="color: #cc9393;">"ruby-growl Notification"</span>]
g.notify <span style="color: #cc9393;">"ruby-growl Notification"</span>, <span style="color: #cc9393;">"It Came From Ruby-Growl"</span>, <span style="color: #cc9393;">"Greetings!"</span>
</pre>
</div>
</div>
</div>
</body>
</html>
Open-source rails apps to study and learn from2010-05-31T00:00:00+00:00https://chrislowis.co.uk/2010/05/31/five-rails-apps-to-study-and-learn-from<p>
If, like me, you learn best by studying other code here's <del>five</del> some
open-source rails apps with something to teach. They're a great
resource to study and improve your own code, or to use as the starting
point for your own applications. All of these applications use Rails 2
but as they are open-source they are a great opportunity to get
your feet wet in the Rails 3 world by helping to port them across.
</p>
<p>
<b>Update:</b> Thanks to the comments here and over on <a href="http://news.ycombinator.com/item%3Fid=1393701">Hacker News</a>, I've
added two more applications and made some comments on the non-standard
nature of the Loved by Less application code.
</p>
<div id="outline-container-1" class="outline-2">
<h2 id="sec-1">Gemcutter </h2>
<div class="outline-text-2" id="text-1">
<p>
A site familiar to all Ruby developers is Gemcutter - the recent
replacement for the venerable Rubyforge. I wasn't aware that the
source code of the site was open source until danieldon <a href="http://news.ycombinator.com/item%3Fid=1394270">pointed it out</a>
to me, but you can study the code <a href="http://github.com/qrush/gemcutter">on github</a>. The Gemcutter source is
written in a very modern way featuring examples of using <a href="http://github.com/qrush/gemcutter/tree/master/features/">Cucumber for integration testing</a>, a simple <a href="http://github.com/qrush/gemcutter/tree/master/app/middleware/">Rack middleware</a> example and an
interesting <a href="http://github.com/qrush/gemcutter/blob/master/config/routes.rb">routes.rb</a> file showing how to cleanly version an API.
</p>
</div>
<div id="outline-container-1_1" class="outline-3">
<h3 id="sec-1_1">Features </h3>
<div class="outline-text-3" id="text-1_1">
<ul>
<li>
A very modern Rails application featuring the latest best practices.
</li>
<li>
Examples of metal and rack middleware.
</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-2" class="outline-2">
<h2 id="sec-2">Spot Us </h2>
<div class="outline-text-2" id="text-2">
<p>
<a href="http://www.spot.us/">Spot Us</a> is a website that allows individuals to commission freelance
journalists. It has many social-network type features which make the
<a href="http://github.com/spot-us/spot-us/">source code</a> interesting reading.
</p>
<img src="/images/spot_us.png" alt="Spot.us screenshot"></img>
<p>
The comprehensive <a href="http://github.com/spot-us/spot-us/tree/5f68f48eef39f5c67ddccd5cdae5feeaaefa320c/spec">test suite</a> is written with rspec and the ruby-like
templating system <a href="http://haml-lang.com/">HAML</a> is used for the views. It's also an example of
how to use jQuery in a rails app instead of the default prototype
through the use of the jrails plugin.
</p>
</div>
<div id="outline-container-2_1" class="outline-3">
<h3 id="sec-2_1">Features </h3>
<div class="outline-text-3" id="text-2_1">
<ul>
<li>
Haml
</li>
<li>
rspec
</li>
<li>
jquery with the jrails plugin.
</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-3" class="outline-2">
<h2 id="sec-3">Loved by Less </h2>
<div class="outline-text-2" id="text-3">
<p>
<a href="http://lovdbyless.com/">Loved By Less</a> is an open-source social network. I think many Rails
freelancers have been asked to create a site with social network
features - profiles, 'friending', photo sharing etc. Less Everything
were no different and created this open-source platform to help ease
the pain of getting started. The <a href="http://github.com/stevenbristol/lovd-by-less/tree/master">source code</a> is available on
github. It's unlikely that simply cloning the app will give you
everything you need, but it's an amazing resource to read through and
take the bits you need for your project.
</p>
<p>
The loved by less source code is starting to show it's age a bit, and
does include some pretty extensive <a href="http://github.com/stevenbristol/lovd-by-less/tree/master/vendor/plugins/less_monkey_patching/lib/">monkey patching</a> which may confuse
a newcomer. While I don't think this is bad in itself (it shows the
flexibility of Ruby, and also the dangers of not following Rails
conventions), it's something to be aware of. I think porting this to
Rails 3 would give newcomers to Rails a great resource to learn from.
</p>
</div>
<div id="outline-container-3_1" class="outline-3">
<h3 id="sec-3_1">Features </h3>
<div class="outline-text-3" id="text-3_1">
<ul>
<li>
Search with Thinking Sphinx
</li>
<li>
Flickr integration
</li>
<li>
Comprehensive email support
</li>
<li>
Paperclip for attachments
</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-4" class="outline-2">
<h2 id="sec-4">Saasy </h2>
<div class="outline-text-2" id="text-4">
<p>
If you're working on a Software as a Service application, take a look
at <a href="http://github.com/maccman/saasy/tree/master">Saasy</a> . It's a template application designed to do billing and
authentication tasks 'so you don't have to'.
</p>
<img src="/images/saasy.png" alt="Sassy screenshot"></img>
<p>
Studying the code base, you'll see examples of using SSL security and
ActiveMerchant. I was also interested in the <a href="http://github.com/maccman/saasy/blob/57686af09acd1d5dddbd07af5dc82efe4a8dcde6/app/models/account.rb">accounts model</a> which
uses the acts_as_state_machine plugin to simplify some of the logic.
</p>
</div>
<div id="outline-container-4_1" class="outline-3">
<h3 id="sec-4_1">Features </h3>
<div class="outline-text-3" id="text-4_1">
<ul>
<li>
acts_as_state_machine
</li>
<li>
ActiveMerchant
</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-5" class="outline-2">
<h2 id="sec-5">Simply Agile </h2>
<div class="outline-text-2" id="text-5">
<p>
<a href="http://github.com/camelpunch/simply_agile">Simply Agile</a> by <a href="http://www.camelpunch.com/">Andrew Bruce</a> is an open source agile software project
management application. Think of a simple version of <a href="http://www.pivotaltracker.com/">Pivotal Tracker</a>. You can add story cards to a backlog, and then assign each
card to a sprint. Each time a card is completed you can drag and drop
it to the appropriate column in the Simply Agile task board. Andrew
has a very dedicated approach to test-driven development and as a
result the application is well covered by cucumber and rspec
tests. The javascript in this application is written in an unobtrusive
style and is a good example of using jQuery for "progressive
enhancement". The app is fully functional in browsers without
javascript, but where javascript is available so is the drag-and-drop
interface.
</p>
</div>
<div id="outline-container-5_1" class="outline-3">
<h3 id="sec-5_1">Features </h3>
<div class="outline-text-3" id="text-5_1">
<ul>
<li>
Cucumber integration tests
</li>
<li>
rspec coverage
</li>
<li>
Drag and drop interface powered by jQuery.
</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-6" class="outline-2">
<h2 id="sec-6">Typo </h2>
<div class="outline-text-2" id="text-6">
<p>
<a href="http://github.com/fdv/typo/tree/master">Typo</a> is "the oldest and most powerful Ruby on Rails blogware" and is
still under active development. While the <a href="http://media.rubyonrails.org/video/rails_blog_2.mov">15 minute blog</a> is still a
fantastic demonstration of how powerful Rails is for rapid application
development, anyone who has decided to take the project further and
create their own fully-featured blog system will know how much extra
work is needed. From comment systems, to anti-spam protection, restful
routing to upload support there are many features that need to be
added. Thankfully Typo has already implemented these and many other
features, and the <a href="http://github.com/fdv/typo/tree/master">source code</a> is available to help you learn how to do
the same.
</p>
</div>
<div id="outline-container-6_1" class="outline-3">
<h3 id="sec-6_1">Features </h3>
<div class="outline-text-3" id="text-6_1">
<ul>
<li>
rspec for testing
</li>
<li>
internationalisation support
</li>
<li>
email notification
</li>
<li>
admin and public-facing interfaces
</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-7" class="outline-2">
<h2 id="sec-7">Open Source Rails </h2>
<div class="outline-text-2" id="text-7">
<p>
<a href="http://www.opensourcerails.com/">Open source rails</a> is a gallery of open-source rails projects. I
selected this project for this list because although the <a href="http://github.com/jcnetdev/opensourcerails/tree/master">code</a> itself
isn't well documented or tested, in many ways this is illustrates a
great strenght of the Rails framework. It is a good project to study
to realise how much can be achieved with a small amount of code and a
handful of powerful <a href="http://github.com/jcnetdev/opensourcerails/tree/master/vendor/plugins/">plugins</a> (not that I'd recommend starting your
next Rails project without tests, of course!).
</p>
</div>
<div id="outline-container-7_1" class="outline-3">
<h3 id="sec-7_1">Features </h3>
<div class="outline-text-3" id="text-7_1">
<ul>
<li>
OpenID integration
</li>
<li>
Paperclip for file uploads
</li>
<li>
A simple set of <a href="http://github.com/jcnetdev/opensourcerails/blob/master/config/routes.rb">routes</a>.
</li>
</ul>
</div>
</div>
</div>
<div id="outline-container-8" class="outline-2">
<h2 id="sec-8">Conclusion </h2>
<div class="outline-text-2" id="text-8">
<p>
I hope you enjoyed this selection of Rails projects. This is far from
a definitive list, and I encourage you to read some of the
suggestions of other apps in the comments below and on the discussion
over on <a href="http://news.ycombinator.com/item%3Fid=1393701">Hacker News.</a> With the upcoming Rails 3 release I think it's
a good time to reflect how far the Rails framework has come, and also
to remember to cater for people who are learning Rails for the first
time - studying open-source code is a great way to learn.
</p>
<p>
Do you have a favourite open-source Rails project? Share it in the
comments below.
</p></div>
</div>
</div>
</body>
</html>
Unfilling regions in Emacs2010-03-03T00:00:00+00:00https://chrislowis.co.uk/2010/03/03/unfill-region-emacs<p>
I have the habit of hitting M-q every 5 seconds or so when working
with text in emacs. This key combination is bound to "fill-paragraph"
and inserts line breaks into the current paragraph to keep the line
width to approximately 80 characters.
</p>
<p>
If you're cutting and pasting the text into an email message or
website form though, this is often not what you want. The way round
this is to "unfill" the current paragraph or region. The following
commands will help:
</p>
<pre class="src src-emacs-lisp"><span style="color: #606060;">(</span><span style="color: #f0dfaf; font-weight: bold;">defun</span> <span style="color: #f0dfaf;">unfill-paragraph</span> <span style="color: #606060;">()</span>
<span style="color: #606060;">(</span>interactive<span style="color: #606060;">)</span>
<span style="color: #606060;">(</span><span style="color: #f0dfaf; font-weight: bold;">let</span> <span style="color: #606060;">((</span>fill-column <span style="color: #606060;">(</span>point-max<span style="color: #606060;">)))</span>
<span style="color: #606060;">(</span>fill-paragraph nil<span style="color: #606060;">)))</span>
<span style="color: #606060;">(</span><span style="color: #f0dfaf; font-weight: bold;">defun</span> <span style="color: #f0dfaf;">unfill-region</span> <span style="color: #606060;">(</span>start end<span style="color: #606060;">)</span>
<span style="color: #606060;">(</span>interactive <span style="color: #cc9393;">"r"</span><span style="color: #606060;">)</span>
<span style="color: #606060;">(</span><span style="color: #f0dfaf; font-weight: bold;">let</span> <span style="color: #606060;">((</span>fill-column <span style="color: #606060;">(</span>point-max<span style="color: #606060;">)))</span>
<span style="color: #606060;">(</span>fill-region start end nil<span style="color: #606060;">)))</span>
</pre>
<p>
But now you have lines in your emacs buffer that wrap in an unusable
way. Longlines-mode to the rescue (M-x longlines-mode). Longlines mode
uses "soft newlines" which will not show up when yanked or saved to
disk.
</p>
<p>
I've recently added my <a href="http://www.github.com/chrislo/emacs-config">emacs config to github</a>. Feel free to have a
poke around and let me know what other emacs tricks I am missing!
</p>
</div>
</body>
</html>
Recent Activities2009-08-25T00:00:00+00:00https://chrislowis.co.uk/2009/08/25/recent-activities<div id="outline-container-1" class="outline-3">
<h3 id="sec-1">Recently… </h3>
<div class="outline-text-3" id="text-1">
<p>
Back in May I started a new job at the BBC. I've been working on some
<a href="http://www.bbc.co.uk/music/introducing">interesting</a> <a href="http://www.bbc.co.uk/music">projects</a> with some great people. I've also been lucky
enough to take part in a couple of Hack Days in an official-ish
capacity, and I intend to blog about some of the technical aspects
soon. For now:
</p>
<ul>
<li>
<a href="http://musichackday.org/hacks.php?page=MusicBore">The Music Bore</a> - an automated DJ system that <a href="http://www.aelius.com/njh/">Nick</a>, <a href="http://www.metade.org/">Patrick</a>, <a href="http://moustaki.org/">Yves</a> and
I built at the Music Hack Day. I wrote some more about this over on
the <a href="http://www.bbc.co.uk/blogs/radiolabs/2009/07/the_music_bore.shtml.">BBC Radio Labs blog</a> Coming soon to an IRC channel, hosted
server, or if Patrick and I get our way Art Installation near you!
</li>
<li>
David and I were invited to the Guardian's Hack Day where we worked
on a RDF and Processing powered mash-up of Guardian and BBC content
around MPs. David <a href="http://www.bbc.co.uk/blogs/bbcinternet/2009/08/at_the_end_of_july.html">blogged</a> about this on the BBC Internet blog.
</li>
</ul>
</div>
</div>
</div>
</body>
</html>
Using R and Ruby - slides from February LRUG2009-02-15T00:00:00+00:00https://chrislowis.co.uk/2009/02/15/LRUG-R-Ruby-talk<div id="outline-container-1" class="outline-3">
<h3 id="sec-1">Introduction </h3>
<div class="outline-text-3" id="text-1">
<p>
Last week I gave a short talk at the <a href="http://lrug.org/meetings/2009/01/20/february-2009-meeting/">February LRUG meeting</a> about using
the scientific programming environment R together with Ruby.
</p>
<p>
My presentation gave a few examples of the kind of statistical
analysis that can be performed with R, and showed how easy this
functionality is to access from within your Ruby code.
</p>
</div>
</div>
<div id="outline-container-2" class="outline-3">
<h3 id="sec-2">Slides </h3>
<div class="outline-text-3" id="text-2">
<embed type="application/x-shockwave-flash" »
src="/images/lrug.swf" width="600" height="450" »
pluginspage="http://www.adobe.com/go/getflashplayer" />
</div>
</div>
<div id="outline-container-3" class="outline-3">
<h3 id="sec-3">Code </h3>
<div class="outline-text-3" id="text-3">
<p>
I'll post the code for the Twitter analysis to github soon. It needs a
little tidying up first.
</p>
<p>
An initial draft of this presentation used R to develop a
recommendation algorithm (using a k-means clustering) using the GitHub
api. Although it worked, the recommendations it made were not great,
so I removed the code from the presentation and replaced it with the
Twitter analysis. I'd like to resurrect this code at some point, so
remember to subscribe to my <a href="http://feeds.feedburner.com/ChrisLowis">RSS feed</a> if you're interested, or nag me
to do it in the comments field below!
</p></div>
</div>
</div>
</body>
</html>
Calculating the Pearson correlation coefficient using R and Ruby2009-01-21T00:00:00+00:00https://chrislowis.co.uk/2009/01/21/pearson-correlation-using-R-and-Ruby<p>
In a <a href="http://blog.chrislowis.co.uk/2008/11/24/ruby-gsl-pearson.html">previous article</a> I talked about using the GNU scientific library
to implement the Pearson correlation algorithm, as used for example in
<a href="http://github.com/maccman/acts_as_recommendable/tree/master.">acts<sub>as</sub><sub>recommendable</sub></a> As a prelude to some forthcoming articles, I'd
like to show you how easy it is to implement the same thing using the
Ruby bindings to the <a href="http://www.r-project.org/">R project</a>.
</p>
<p>
For this to work you'll need to install <a href="http://www.r-project.org/">R</a> and the <a href="http://web.kuicr.kyoto-u.ac.jp/~alexg/rsruby/">rsruby</a> gem. Take a
look at the documentation for the rsruby gem, as while installation is
straightforward there's a couple of things to be aware of.
</p>
<p>
Having done that, let's reopen the Pearson class from the <a href="http://blog.chrislowis.co.uk/2008/11/24/ruby-gsl-pearson.html">previous article</a> and add a new R-based method
</p>
<pre class="src src-ruby"><span style="color: #f0dfaf; font-weight: bold;">class</span> <span style="color: #dfdfbf; font-weight: bold;">Pearson</span>
<span style="color: #f0dfaf; font-weight: bold;">def</span> <span style="color: #f0dfaf;">initialize</span>
require <span style="color: #cc9393;">'rsruby'</span>
<span style="color: #f0dfaf;">@r</span> = <span style="color: #dfdfbf; font-weight: bold;">RSRuby</span>.instance
<span style="color: #f0dfaf; font-weight: bold;">end</span>
<span style="color: #f0dfaf; font-weight: bold;">def</span> <span style="color: #f0dfaf;">R_pearson</span>(x,y)
<span style="color: #f0dfaf;">@r</span>.cor(x,y)
<span style="color: #f0dfaf; font-weight: bold;">end</span>
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
The new initialize method sets up the communication with the R
instance. The actual definition for the Pearson method simple,
conversion from Ruby Arrays to R vectors is handled automatically by
the bindings, you simply need to know the correct R method to call -
in this case 'cor'. Take a look through the R manual to learn more
about this powerful tool - almost all the features are accessible
through the Ruby bindings.
</p>
<p>
Here's a quick modification to the benchmark to compare performances:
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'benchmark'</span>
n = 100000
x = []; n.times{x << rand}
y = []; n.times{y << rand}
p = <span style="color: #dfdfbf; font-weight: bold;">Pearson</span>.new()
<span style="color: #dfdfbf; font-weight: bold;">Benchmark</span>.bm() <span style="color: #f0dfaf; font-weight: bold;">do</span> |bm|
bm.report(<span style="color: #cc9393;">"Ruby:"</span>) {p.ruby_pearson(x,y)}
bm.report(<span style="color: #cc9393;">"GSL:"</span>) {p.gsl_pearson(x,y)}
bm.report(<span style="color: #cc9393;">"Inline:"</span>) {p.inline_pearson(n,x,y)}
bm.report(<span style="color: #cc9393;">"R:"</span>) {p.<span style="color: #dfdfbf; font-weight: bold;">R_pearson</span>(x,y)}
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
And the results,
</p>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption></caption>
<colgroup><col class="left" /><col class="right" /><col class="right" /><col class="right" /><col class="left" />
</colgroup>
<tbody>
<tr><td class="left"></td><td class="right">user</td><td class="right">system</td><td class="right">total</td><td class="left">real</td></tr>
<tr><td class="left">Ruby:</td><td class="right">1.590000</td><td class="right">0.020000</td><td class="right">1.610000</td><td class="left">(1.610470)</td></tr>
<tr><td class="left">GSL:</td><td class="right">0.010000</td><td class="right">0.000000</td><td class="right">0.010000</td><td class="left">(0.062538)</td></tr>
<tr><td class="left">Inline:</td><td class="right">0.010000</td><td class="right">0.000000</td><td class="right">0.010000</td><td class="left">(0.004548)</td></tr>
<tr><td class="left">R:</td><td class="right">0.220000</td><td class="right">0.010000</td><td class="right">0.230000</td><td class="left">(0.227184)</td></tr>
</tbody>
</table>
<p>
The R version is around 7 times faster than the native Ruby version,
but not as fast as the C-based approaches <a href="http://blog.chrislowis.co.uk/2008/11/24/ruby-gsl-pearson.html">described earlier</a>.
</p>
<p>
The real power of interfacing with R however is in the ability to
quickly swap out one algorithm for another, or experiment
interactively with your data in an irb console. Once you have an
algorithm that works well in your case, then it may be necessary to
re-implement in a faster language if performance is a concern.
</p>
<p>
I'll be talking about R and Ruby a little more in the future, so
subscribe to the <a href="http://feeds.feedburner.com/ChrisLowis">RSS feed</a> if you're interested.
</p>
</div>
</body>
</html>
Identify Programming Languages with SourceClassifier2009-01-04T00:00:00+00:00https://chrislowis.co.uk/2009/01/04/identify-programming-languages-with-source-classifier<p>
Do you need to identify the programming language used in a snippet of
code? For example, in a <a href="http://pastie.org/">pastie</a> style application or in your blog
comments system. I've just released version 0.2.1 of SourceClassifier
over on <a href="http://github.com/chrislo/sourceclassifier/tree/master">github</a>.
</p>
<p>
Source classifier identifies programming language using a Bayesian
classifier trained on a corpus generated from the "Computer Language
Benchmarks Game":<a href="http://shootout.alioth.debian.org/">http://shootout.alioth.debian.org/</a>. It is written in
Ruby and available as a gem. Out of the box SourceClassifier
recognises C, Java, Javascript, Perl, Python and Ruby. A nice
advantage of using a Bayesian classifier to identify the source code
is that even false matches will still give some usable
highlighting. To train the classifier to identify new languages
download the sources from <a href="http://github.com/chrislo/sourceclassifier/tree/master">github</a> .
</p>
<div id="outline-container-1" class="outline-3">
<h3 id="sec-1">Usage </h3>
<div class="outline-text-3" id="text-1">
<p>
First install the gem using github as a source
</p>
<pre class="src src-ruby">$ gem sources -a http:<span style="color: #cc9393;">//</span>gems.github.com
$ sudo gem install chrislo-sourceclassifier
</pre>
<p>
Then, to use
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'rubygems'</span>
require <span style="color: #cc9393;">'sourceclassifier'</span>
s = <span style="color: #dfdfbf; font-weight: bold;">SourceClassifier</span>.new
ruby_text = <span style="color: #cc9393;"><<EOT
def my_sorting_function(a)
a.sort
end
EOT</span>
c_text = <span style="color: #cc9393;"><<EOT
#include <unistd.h>
int main() {
write(1, "hello world\n", 12);
return(0);
}
EOT</span>
s.identify(ruby_text) <span style="color: #708070;">#</span><span style="color: #7f9f7f;">=> Ruby
</span>s.identify(c_text) <span style="color: #708070;">#</span><span style="color: #7f9f7f;">=> Gcc
</span></pre>
</div>
</div>
<div id="outline-container-2" class="outline-3">
<h3 id="sec-2">Training </h3>
<div class="outline-text-3" id="text-2">
<p>
Download the sources from github and in the directory run the training
rake test
</p>
<pre class="src src-ruby">$ rake train
</pre>
<p>
In the ./sources directory are sub-directories for each language you
wish to identify. Each sub-directory contains examples of programs
written in that language. The name of the directory is significant -
it is the value returned by the SourceClassifier.identify() method.
</p>
<p>
The rake task populate can be used to build these sub-directories from
a checkout of the <a href="http://alioth.debian.org/scm/?group_id=30402">computer language shootout sources</a> but you are free
to train the classifier using any available examples.
</p>
</div>
</div>
<div id="outline-container-3" class="outline-3">
<h3 id="sec-3">Acknowledgments </h3>
<div class="outline-text-3" id="text-3">
<p>
This library depends heavily on the great <a href="http://classifier.rubyforge.org/">Classifier</a> gem by Lucas
Carlson and David Fayram II.
</p>
</div>
</div>
</div>
</body>
</html>
Get working quickly with a customised Rails project launcher2008-12-30T00:00:00+00:00https://chrislowis.co.uk/2008/12/30/get-started-quickly-rails-project-launcher<p>
I just knocked up a quick Ruby script to automate some of the steps I
always do when starting work on a Rails project, namely:
</p><ul>
<li>
open an iTerm tab running script/server
</li>
<li>
open an iTerm tab running script/console
</li>
<li>
open an iTerm tab running autotest
</li>
<li>
open an iTerm tab running a shell window
</li>
<li>
launch Safari on localhost:3000 for testing.
</li>
</ul>
<p>
Customise this little script to suit your own needs. You'll need to
install the rb-appscript gem to start with, and obviously this only
applies to OS X users.
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'rubygems'</span>
require <span style="color: #cc9393;">'appscript'</span>
<span style="color: #dfdfbf; font-weight: bold;">RAILS_PROJECT_PATH</span> = <span style="color: #cc9393;">"~/path_to_your_rails_project"</span> <span style="color: #708070;"># </span><span style="color: #7f9f7f;">Customise this
</span>
<span style="color: #f0dfaf; font-weight: bold;">def</span> <span style="color: #f0dfaf;">create_iterm_tab</span>( command = <span style="color: #cc9393;">""</span> )
<span style="color: #f0dfaf;">@iterm</span> ||= <span style="color: #dfdfbf; font-weight: bold;">Appscript</span>::app( <span style="color: #cc9393;">'iTerm'</span> )
session = <span style="color: #f0dfaf;">@iterm</span>.current_terminal.sessions.end.make( <span style="color: #dca3a3; font-weight: bold;">:new</span> => <span style="color: #dca3a3; font-weight: bold;">:session</span> )
session.exec( <span style="color: #dca3a3; font-weight: bold;">:command</span> => <span style="color: #cc9393;">'bash -l'</span> )
session.write( <span style="color: #dca3a3; font-weight: bold;">:text</span> => <span style="color: #cc9393;">"cd </span><span style="color: #f0dfaf;">#{RAILS_PROJECT_PATH}</span><span style="color: #cc9393;">"</span> )
session.write( <span style="color: #dca3a3; font-weight: bold;">:text</span> => command ) <span style="color: #f0dfaf; font-weight: bold;">unless</span> command.nil?
<span style="color: #f0dfaf; font-weight: bold;">end</span>
<span style="color: #f0dfaf; font-weight: bold;">def</span> <span style="color: #f0dfaf;">launch_browser</span>( url = <span style="color: #cc9393;">"http://localhost:3000"</span>)
<span style="color: #f0dfaf;">@safari</span> ||= <span style="color: #dfdfbf; font-weight: bold;">Appscript</span>::app(<span style="color: #cc9393;">'Safari'</span>)
<span style="color: #f0dfaf;">@safari</span>.open_location(url)
<span style="color: #f0dfaf; font-weight: bold;">end</span>
create_iterm_tab(<span style="color: #cc9393;">"./script/server"</span>)
create_iterm_tab(<span style="color: #cc9393;">"./script/console"</span>)
create_iterm_tab(<span style="color: #cc9393;">"autotest"</span>)
create_iterm_tab()
sleep(5)
launch_browser()
</pre>
<p>
The sleep(5) is in there to give time for the server to spin up before
launching the browser.
</p>
<p>
Now put all this in a file called "launch<sub>project</sub>.command" on the
Desktop, and chmod +x it to allow it to allow it to execute when
double-clicked.
</p>
<p>
I got some tips from <a href="http://logaan.wordpress.com/2008/01/16/quicksilver-rails-project-opener-revisited/">Dribblings of a Deranged Hermit</a>.
</p>
<p>
Happy New Year!
</p>
</div>
</body>
</html>
Fitting curves to data using Ruby and the GNU Scientific Library2008-12-01T00:00:00+00:00https://chrislowis.co.uk/2008/12/01/curve-fit-with-ruby-gsl<p>
In this post I'll show you how to use the <a href="http://www.gnu.org/software/gsl/">GNU Scientific Library</a> and
its <a href="http://rb-gsl.rubyforge.org/">Ruby bindings</a> to fit curves to data. This technique is useful if,
for example, you want to extrapolate into the future on the basis of
some past information.
</p>
<p>
By way of example, your boss approaches you with some historic revenue
figures from your new Web 2.0 venture, and asks you to predict future
growth. Let's generate some example data to play with:
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'gsl'</span>
time = <span style="color: #dfdfbf; font-weight: bold;">GSL</span>::<span style="color: #dfdfbf; font-weight: bold;">Vector</span>.linspace(0,24,100)
revenue = <span style="color: #dfdfbf; font-weight: bold;">GSL</span>::<span style="color: #dfdfbf; font-weight: bold;">Vector</span>.linspace(0,1,100).collect{|yi| yi+rand()/10}
</pre>
<p>
In this code we're using the GSL Vector class to represent time. Here
we have a vector of months representing two years of data (24
months). The linspace method causes our vector to have 100 evenly
spaced elements between 0 and 24.
</p>
<p>
We concoct some revenue data using GSL::Vector again. The GSL Vector
class does not implement all of methods found in Ruby's native array
class, but does have 'collect'. Here we create a random scattering of
points.
</p>
<p>
Let's visualise this data first to see what we are dealing with. To do
this we have a number of options, here I'd like to show how we can use
Gnuplot to do the visualisation.
</p>
<p>
First install the gnuplot gem:
</p>
<pre class="src src-ruby">$ gem install gnuplot
</pre>
<p>
You'll need to have <a href="http://www.gnuplot.info/">gnuplot</a> installed to use the bindings. Binary
packages are available for many platforms, just make sure that gnuplot
is in your path after installation. OS X users should install <a href="http://sourceforge.net/projects/aquaterm/">aquaterm</a>
to allow plotting to a desktop window.
</p>
<p>
Now let's plot the data generated above:
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'rubygems'</span>
require <span style="color: #cc9393;">'gnuplot'</span>
<span style="color: #dfdfbf; font-weight: bold;">Gnuplot</span>.open <span style="color: #f0dfaf; font-weight: bold;">do</span> |gp|
<span style="color: #dfdfbf; font-weight: bold;">Gnuplot</span>::<span style="color: #dfdfbf; font-weight: bold;">Plot</span>.new( gp ) <span style="color: #f0dfaf; font-weight: bold;">do</span> |plot|
plot.title <span style="color: #cc9393;">"Company turnover"</span>
plot.xlabel <span style="color: #cc9393;">"Month"</span>
plot.ylabel <span style="color: #cc9393;">"Billions $"</span>
plot.data << <span style="color: #dfdfbf; font-weight: bold;">Gnuplot</span>::<span style="color: #dfdfbf; font-weight: bold;">DataSet</span>.new( [time.to_a, revenue.to_a] ) <span style="color: #f0dfaf; font-weight: bold;">do</span> |ds|
ds.with = <span style="color: #cc9393;">"points"</span>
ds.notitle
<span style="color: #f0dfaf; font-weight: bold;">end</span>
plot.terminal <span style="color: #cc9393;">"svg"</span>
plot.output <span style="color: #cc9393;">"revenue.svg"</span>
<span style="color: #f0dfaf; font-weight: bold;">end</span>
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
The code above generates the following graphic in SVG format.
</p>
<img src="/images/revenue.jpg" alt="revenue curve"></img>
<p>
Gnuplot has a number of output formats, known as "terminals" to choose
from including one which plots straight to screen - great for rapid
exploration of data.
</p>
<p>
Looking at the data, we see that a straight line drawn through the
points would be a good model for the growth in revenue. We can use
GSL's line-fitting tools to perform this fit for us:
</p>
<pre class="src src-ruby">(c0, c1, cov00, cov01, cov11, chisq, status) = <span style="color: #dfdfbf; font-weight: bold;">GSL</span>::<span style="color: #dfdfbf; font-weight: bold;">Fit</span>::linear(time,revenue)
revenue_fit = (time * c1) + c0
puts c1
puts c0
</pre>
<p>
The value of c1 is the growth rate per month, in this case around 0.04
Billion $ per month! We have used a linear fit here, but the GSL
provides <a href="http://rb-gsl.rubyforge.org/fit.html">many</a> <a href="http://rb-gsl.rubyforge.org/nonlinearfit.html">other</a> <a href="http://rb-gsl.rubyforge.org/bspline.html">fitting methods</a> for more complicated data. A quick
plot checks the goodness of our fit:
</p>
<pre class="src src-ruby">plot.data << <span style="color: #dfdfbf; font-weight: bold;">Gnuplot</span>::<span style="color: #dfdfbf; font-weight: bold;">DataSet</span>.new( [time.to_a, revenue_fit.to_a] ) <span style="color: #f0dfaf; font-weight: bold;">do</span> |ds|
ds.with = <span style="color: #cc9393;">"lines"</span>
ds.notitle
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
Add this snippet of code to the plotting function above (after the
first plot.data and before the call to plot.terminal)
</p>
<img src="/images/fitted.jpg" alt="fit curve"></img>
<p>
If you need to perform data analysis, provide graphics for your users
in your webapp, or produce high quality plots I encourage you to
investigate the combination of ruby, GSL and GNUPlot.
</p>
</div>
</body>
</html>
Implementing the Pearson correlation algorithm using Ruby and the GNU Scientific Library2008-11-24T00:00:00+00:00https://chrislowis.co.uk/2008/11/24/ruby-gsl-pearson<p>
At <a href="http://rubymanor.org/">Ruby Manor</a> on Saturday <a href="http://www.eribium.org/blog/">Alex MacCaw</a> gave a great introduction to his
<a href="http://github.com/maccman/acts_as_recommendable/tree/master">acts_as_recommendable</a> plugin for Rails. acts_as_recommendable
simplifies collaborative filtering for Rails models, automatically
generating recommended items, at an on-line store for example, based
on a database of user preferences.
</p>
<p>
At its heart, acts_as_recommendable uses a statistical measure known
as the Pearson correlation coefficient to calculate the "nearness" of
items to one another. Alex talked about the performance issues he
encountered when implementing the algorithm in pure ruby. To allow
recommendations to be calculated for the entire database he had to
switch to making calculations offline and reimplementing the algorithm
in C using <a href="http://rubyforge.org/projects/rubyinline/">RubyInline</a>.
</p>
<p>
The <a href="http://www.gnu.org/software/gsl/">GNU Scientific Library</a> has an implementation of the Pearson
algorithm, and in this post I'd like to show how the Ruby code, or its
inline-C equivalent can be replaced with GSL code using Ruby bindings
to the GSL.
</p>
<p>
My naive pure Ruby version of the algorithm looks like this:
</p>
<pre class="src src-ruby"><span style="color: #f0dfaf; font-weight: bold;">def</span> <span style="color: #f0dfaf;">ruby_pearson</span>(x,y)
n=x.length
sumx=x.inject(0) {|r,i| r + i}
sumy=y.inject(0) {|r,i| r + i}
sumxSq=x.inject(0) {|r,i| r + i**2}
sumySq=y.inject(0) {|r,i| r + i**2}
prods=[]; x.each_with_index{|this_x,i| prods << this_x*y[i]}
pSum=prods.inject(0){|r,i| r + i}
<span style="color: #708070;"># </span><span style="color: #7f9f7f;">Calculate Pearson score
</span> num=pSum-(sumx*sumy/n)
den=((sumxSq-(sumx**2)/n)*(sumySq-(sumy**2)/n))**0.5
<span style="color: #f0dfaf; font-weight: bold;">if</span> den==0
<span style="color: #f0dfaf; font-weight: bold;">return</span> 0
<span style="color: #f0dfaf; font-weight: bold;">end</span>
r=num/den
<span style="color: #f0dfaf; font-weight: bold;">return</span> r
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
Here I should note that the acts<sub>as</sub><sub>recommendable</sub> code is considerably
more complicated, however the heart of the calculation looks something
like the above. We can replace that with an inline C version using
something like:
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'rubygems'</span>
require <span style="color: #cc9393;">'inline'</span>
inline <span style="color: #f0dfaf; font-weight: bold;">do</span> |builder|
builder.c <span style="color: #cc9393;">'
#include <math.h>
double inline_pearson(int n, VALUE x, VALUE y) {
double sum1 = 0.0;
double sum2 = 0.0;
double sum1Sq = 0.0;
double sum2Sq = 0.0;
double pSum = 0.0;
VALUE *x_a = RARRAY(x)->ptr;
VALUE *y_a = RARRAY(y)->ptr;
int i;
for(i=0; i<n; i++) {
double this_x;
double this_y;
this_x = NUM2DBL(x_a[i]);
this_y = NUM2DBL(y_a[i]);
sum1 += this_x;
sum2 += this_y;
sum1Sq += pow(this_x, 2);
sum2Sq += pow(this_y, 2);
pSum += this_y * this_x;
}
double num;
double den;
num = pSum - ( ( sum1 * sum2 ) / n );
den = sqrt( ( sum1Sq - ( pow(sum1, 2) ) / n ) *
( sum2Sq - ( pow(sum2, 2) ) / n ) );
if(den == 0){
return 0.0;
} else {
return num / den;
}
}'</span>
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
Which is a considerable amount of code. I love the fact that this code
can be embedded directly in the Ruby source code but, as was pointed
out to me, for some people this would be seen as something of a
maintenance nightmare. If you're prepared to install the pre-requisite
GSL library and its <a href="http://rb-gsl.rubyforge.org/">Ruby bindings</a> you can actually replace all of the
above code with the simple:
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'gsl'</span>
<span style="color: #f0dfaf; font-weight: bold;">def</span> <span style="color: #f0dfaf;">gsl_pearson</span>(x,y)
<span style="color: #dfdfbf; font-weight: bold;">GSL</span>::<span style="color: #dfdfbf; font-weight: bold;">Stats</span>::correlation(
<span style="color: #dfdfbf; font-weight: bold;">GSL</span>::<span style="color: #dfdfbf; font-weight: bold;">Vector</span>.alloc(x),<span style="color: #dfdfbf; font-weight: bold;">GSL</span>::<span style="color: #dfdfbf; font-weight: bold;">Vector</span>.alloc(y)
)
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
In this code, the <verb>GSL::Vector.alloc()</verb> method converts a
Ruby Array to a GSL Vector class. We then call the correlation method,
helpfully provided by the <a href="http://rb-gsl.rubyforge.org/stats.html">Statistics</a> portion of the GSL.
</p>
<p>
So, how does this perform ? A quick benchmark :
</p>
<pre class="src src-ruby">require <span style="color: #cc9393;">'benchmark'</span>
n = 100000
x = []; n.times{x << rand}
y = []; n.times{y << rand}
<span style="color: #dfdfbf; font-weight: bold;">Benchmark</span>.bm() <span style="color: #f0dfaf; font-weight: bold;">do</span> |bm|
bm.report(<span style="color: #cc9393;">"Ruby:"</span>) {ruby_pearson(x,y)}
bm.report(<span style="color: #cc9393;">"GSL:"</span>) {gsl_pearson(x,y)}
bm.report(<span style="color: #cc9393;">"Inline:"</span>) {inline_pearson(n,x,y)}
<span style="color: #f0dfaf; font-weight: bold;">end</span>
</pre>
<p>
Gives some indicative results :
</p>
<table border="2" cellspacing="0" cellpadding="6" rules="groups" frame="hsides">
<caption></caption>
<colgroup><col class="left" /><col class="right" /><col class="right" /><col class="right" /><col class="left" />
</colgroup>
<thead>
<tr><th scope="col" class="left"></th><th scope="col" class="right">user</th><th scope="col" class="right">system</th><th scope="col" class="right">total</th><th scope="col" class="left">real</th></tr>
</thead>
<tbody>
<tr><td class="left">Ruby:</td><td class="right">1.530000</td><td class="right">0.020000</td><td class="right">1.550000</td><td class="left">(1.544765)</td></tr>
<tr><td class="left">GSL:</td><td class="right">0.010000</td><td class="right">0.000000</td><td class="right">0.010000</td><td class="left">(0.015925)</td></tr>
<tr><td class="left">Inline:</td><td class="right">0.000000</td><td class="right">0.000000</td><td class="right">0.000000</td><td class="left">(0.004115)</td></tr>
</tbody>
</table>
<p>
While the Inline version in this example outperforms the GSL version,
both offer considerable savings on the Ruby version. One of the real
advantages of the GSL is it allows you to rapidly experiment with
alternative implementations of an algorithm. Also you can be safe in
the knowledge that your code is based on a well-tested library of
scientific functions.
</p>
</div>
</body>
</html>