Hackermonthly Issue051
Hackermonthly Issue051
Curator
Lim Cheng Soon
Contributors
Andrew Kelley
Yu Jiang Tham
Chris Loukas
Hadi Hariri
Robert Muth
Brian Green
Proofreaders
Advertising
Published by
Emily Griffin
Sigmarie Soto
Ebook Conversion
Contact
Netizens Media
46, Taylor Road,
11600 Penang,
Malaysia.
Printer
MagCloud
Hacker Monthly is published by Netizens Media and not affiliated with Y Combinator in any way.
Contents
FEATURE
Kelley
PROGRAMMING
14
24
27
30
SPECIAL
34
FEATURE
4 FEATURE
A Short Explanation of
Loudness Compensation
2.0 [hn.my/amarok20]
2.1 [hn.my/amarok21]
2.2 [hn.my/amarok22]
6 FEATURE
Filter Search
There should be a text box where I
can type search terms and instantly
see the search results live. And
it should ignore diacritics. For
example, I could type jonsi ik and
match the song Boy Lilikoi by Jnsi.
PartyBeat
It would be a long time before my
wish list of features would become
a reality. Meanwhile, back in college
my buddy made a fun little project
[hn.my/partybeat] which served as
a music player that multiple people
could control at the same time with
a web interface. He installed it in
my and my roommates apartment,
and the three of us used it in our
apartment as a shared jukebox;
anarchy deciding what we would
listen to while we worked on our
respective jobs, homework, or projects. We dubbed it PartyBeat.
Heres a screenshot:
Dynamic Mode
Party Mode
DJ Mode
8 FEATURE
<groove/groove.h>
<groove/encoder.h>
<stdio.h>
<string.h>
<stdlib.h>
10 FEATURE
encoder->codec_short_name = codec;
encoder->filename = output_file_name;
encoder->mime_type = mime;
if (groove_playlist_count(playlist) == 1) {
groove_file_audio_format(playlist->head->file, &encoder->target_audio_format);
// copy metadata
struct GrooveTag *tag = NULL;
while((tag = groove_file_metadata_get(playlist->head->file, "", tag, 0))) {
groove_encoder_metadata_set(encoder, groove_tag_key(tag), groove_tag_value(tag), 0);
}
}
if (groove_encoder_attach(encoder, playlist) < 0) {
fprintf(stderr, "error attaching encoder\n");
return 1;
}
FILE *f = fopen(output_file_name, "wb");
if (!f) {
fprintf(stderr, "Error opening output file %s\n", output_file_name);
return 1;
}
struct GrooveBuffer *buffer;
while (groove_encoder_buffer_get(encoder, &buffer, 1) == GROOVE_BUFFER_YES) {
fwrite(buffer->data[0], 1, buffer->size, f);
groove_buffer_unref(buffer);
}
fclose(f);
groove_encoder_detach(encoder);
groove_encoder_destroy(encoder);
struct GroovePlaylistItem *item = playlist->head;
while (item) {
struct GrooveFile *file = item->file;
struct GroovePlaylistItem *next = item->next;
groove_playlist_remove(playlist, item);
groove_file_close(file);
item = next;
}
groove_playlist_destroy(playlist);
return 0;
}
11
Packaging
Nothing turns away potential users
faster than a cumbersome install
process. I knew that I had to make
Groove Basin easy to install, so I
took several steps to make it so.
One thing I did was bundle some
of the harder-to-find dependencies
along with it. Specifically, libav10,
libebur128, and SDL2. This way if
the user is on a computer that does
not have those packages
readily available, they may
still install libgroove.
This convenience is less
desirable than relying on
existing system dependencies, however, so if the
configure script detects
system libraries, it happily
prefers them.
Next, I made a libgroove PPA for
Ubuntu users. This makes installing
libgroove as easy as:
sudo apt-add-repository
ppa:andrewrk/libgroove
sudo apt-get update
sudo apt-get install libgroove-dev libgrooveplayer-dev
libgrooveloudness-dev
12 FEATURE
Conclusion
3 years, 6 months from git init and
Groove Basin is still under active
development. Heres what the UI
looks like today:
Last.fm scrobbling.
13
PROGRAMMING
14 PROGRAMMING
Clear tubing
Arduino Nano
5x TIP120 w/ Diodes
5x 2.2kOhm resistor
Design
Bar Mixvah is designed to use a
system of 5 peristaltic pumps that
are switched by 5 bipolar junction
transistors (TIP120), all controlled
by an Arduino, which itself is controlled by the Johnny-Five package
on the node.js/express web server
that is running on your laptop/Windows tablet (or maybe Raspberry
Pi? I havent tried). Having it on
a web server allows users to order
from any device, be it a phone,
tablet, or other laptop that can
connect to your WiFi access points
internal network. Practicality-wise,
maybe its not necessary. However,
in my experience, people seem to
enjoy ordering from a tablet that
theyre holding in their hands more
Soldering
Before connecting any wires, youll
want to do all of the soldering.
Youll have to solder the 5.5mm
x 2.1mm coaxial power connector to two jumper wires, one for
the positive lead and one for the
negative lead. Plug in your 12V DC
power supply to the wall, then plug
the coaxial power connector into
the DC power supply. Use your
multimeter to find out which lead
is positive and negative by placing
the probes on two of the leads until
you find that the multimeter says
12V; those are your positive and
negative leads (if it says -12V, then
youve got the positive and negative leads switched). Unplug the
coaxial power connector. Strip two
wires and solder one to each of the
coaxial power connectors leads.
After youre done soldering, wrap
any exposed metal around the leads
in electrical tape.
Next, youll want to solder
wires to the leads of the peristaltic
pumps. The positive lead of the
pump should be labeled, so you
should not need to guess. If it is
not labeled, you will just need to
make note of which way the pump
is turning and make sure that all
of them are turning in the same
15
Assembly
Heres the wiring diagram for the
robot. As you can see, its relatively
simple:
Wiring
Heres where it gets a little
bit tricky. The actual wiring
is not too complicated,
but it requires a little bit
of finesse due to the confines of space that we are
working with. Since we are
fitting everything on a single
breadboard, we need to
ensure everything is placed
in the right spot.
In case you havent used
a breadboard in a while,
each of the numbers running down the breadboard
indicate an individual node.
The center divides the two sides, so
they are separate nodes. The (+) rail
running up the left and right side of
the breadboard is one node per side,
and it is the same with the (-) rail.
The first thing that you should
do before getting any wiring done
is to hook up your pumps individually and ensure they are all working.
The photo below shows a little bit
more complex of a circuit. To check
if its working, you can just connect
the coaxial power connector and
pump on a breadboard and plug
in the power and ensure that the
pump works.
16 PROGRAMMING
Software Design
In this section, I will be discussing
the software design of the robot,
as well as the considerations that I
took in designing it. Im assuming
that you have some working knowledge of JavaScript and MongoDB. I
realize that the Angular.js code that
is written here may not be the most
efficient, since the whole point of
me building the robot was to learn
Angular (and also make something
awesome). Therefore, if you spot
any changes that need to be made
to the code to increase efficiency or
whatnot, please let me know in the
comments or send a pull request to
the Bar Mixvah GitHub.
17
p2: pump2,
p3: pump3,
p4: pump4
});
General Flow
Heres a pretty simplified design that gives you a general picture of how the code works with the physical
elements:
18 PROGRAMMING
Choosing Pumps
The pump system is set up so that as soon as the any
of the pump ingredients are changed, the entire pumps
object (containing all of the individual pump ingredients) is changed. The ng-click directive in this case calls
two functions. One function saves the pumps object
by overriding the previous pumps object, the other
figures out the number of duplicate ingredients and
writes the number of duplicates that are checked at
other times (such as when the Make button is pressed).
The reason why we dont just check pumps for duplicates immediately is if, say you are a user and you want
to move Orange Juice from pump0 to pump2. You
might change pump2 to Orange Juice first, but if that
throws an error since Orange Juice is also currently
on pump0, that is not a very good user experience.
> views/index.jade
div.pumpContainer(ng-repeat="pump in pumps.
ingredients")
select.mixers(ng-change="savePumpValue($index);
writeNumDuplicates()", ng-model="pump.ingredient", ng-options="i for i in ingredientsList")
19
Pump Operation
The pumps are switched on/off by the 5V from the
Arduino pins going to the each of the TIP120 transistors,
which in turn switch the 12V for the individual pumps.
Since the Johnny-Five package contains a simple interface for LEDs, I decided to use its switch on/off properties for switching the pumps because its just a simple
digitalWrite(HIGH/LOW). Heres the code for it:
Making a Drink
The simplicity of the UI hides much of the complexity
behind the actual making of a drink. We want to make
a drink that is top-biased in terms of ingredients. That
is, all of the ingredients with smaller amounts should
be on top so that gravity will cause them to mix into
the drink. This adds a little bit of complexity in that we
need to also pass a delay amount for each pump, but
it is worth it for a drink that is mixed better! Heres a
diagram of how the pump timings will work out:
> public/javascripts/robot/backend.js
pump0 = new five.Led(7);
pump1 = new five.Led(6);
pump2 = new five.Led(5);
pump3 = new five.Led(4);
pump4 = new five.Led(3);
20 PROGRAMMING
}
this.selectedDrink = 'selectedDrink';
$scope.lastSelected = this;
};
21
largestAmount = ingredients[i].amount;
largestIndex = i;
}
// Append pump numbers to the ingredients
for (var j in pumps.ingredients) {
if (ingredients[i].name === pumps.
ingredients[j].ingredient) {
ingredients[i].pump = pumps.ingredients[j].
label;
continue;
}
}
}
After all of this, in the code below, you will see that
we get the normalization factor, which is the drinkSize
divided by the total amount of all drinks. With this normalization factor, we can multiply the largest amount
of drink by this value in order to get the total pump
time (since pumps will be running in parallel, the total
pump time is the pump time of the ingredient with the
highest amount). If you recall from above, this is the
$scope.pumpTime that we delayed 200ms to get on
the front end. After this, we modify the amounts all of
the ingredients in the array based on the normalization
factor, and add the delay so that we can top-weight the
ingredients in the drink. At the end, we use socket.io to
pass the ingredients object to the backend.
> public/javascripts/robot/frontend.js (continuation of makeDrink function)
// Normalize
var normFactor = drinkSize/amountTotal;
var totalPumpMilliseconds = parseInt(normFactor
* largestAmount);
$scope.pumpTime = totalPumpMilliseconds;
// Set the normalized amount and delay for each
ingredient
ingredients[largestIndex].amount =
parseInt(normFactor * Number(ingredients[largest
Index].amount));
ingredients[largestIndex].delay = 0;
for (var i in ingredients) {
if (i === largestIndex) continue;
ingredients[i].amount = parseInt(normFactor *
Number(ingredients[i].amount));
ingredients[i].delay =
22 PROGRAMMING
ingredients[largestIndex].amount ingredients[i].amount;
}
socket.emit("Make Drink", ingredients);
}
23
Jenkins
Our first stop for our testing suite
is Jenkins. Weve used Jenkins in
other projects before and we feel
comfortable with it since it is
mature, widely used, and provides
great flexibility through its plugin
system. Jenkins has very good
Github integration, which is another plus for us. It
is quite simple to set it up so that every commit in a
branch will trigger the tests.
24 PROGRAMMING
Test the deployment of the app in a staging environment where you want to ensure backwards
compatibility.
Enter Docker
Docker helps you easily
create lightweight,
portable, self-sufficient
containers from any
application. It is fast,
reliable, and a perfect
fit for our needs. The
idea is to set up Jenkins
so that every pull
request to a specific
branch (e.g., staging) triggers a job. Jenkins then commands our testing infrastructure to spawn different
Docker applications simultaneously and runs the tests
in parallel.
And we will end up with an ubuntu image/application. We can then run the Docker container by typing:
25
We just told Ansible that we have three test servers, grouped as testservers. For each one the user is
mister and the ssh key is testkey, as defined in the
[testservers:vars] section.
Each test server should have Docker installed and a
specified Docker image built and ready for use. To do
that, we have to define some playbooks and roles:
- name: Install new kernel
sudo: True
apt:
pkg: "{{ item }}"
state: latest
update-cache: yes
with_items:
- linux-image-generic-lts-raring
- linux-headers-generic-lts-raring
register: kernel_result
- name: Reboot instance if kernel has changed
sudo: True
command: reboot
register: reboot_result
when: "kernel_result|changed"
- name: Wait for instance to come online
sudo: False
local_action: wait_for host={{ ansible_ssh_
host }} port=22 state=started
when: "reboot_result|success"
- name: Add Docker repository key
sudo: True
apt_key: url="https://get.docker.io/gpg"
- name: Add Docker repository
sudo: True
apt_repository:
repo: 'deb http://get.docker.io/ubuntu
docker main'
update_cache: yes
- name: Install Docker
sudo: True
apt: pkg=lxc-docker state=present
notify: "Start Docker"
- name: Make dir for io docker files
command: mkdir -p docker/iotest
- name: Copy io Dockerfiles
template:
src=templates/iotest/Dockerfile.j2
dest=docker/iotest/Dockerfile
- name: Copy io init scripts
copy: src=templates/iotest/init.sh
dest=docker/iotest/init.sh
- name: Build docker images for io
sudo: True
command: docker build -t mist/iotest docker/
iotest
Thats it.
We have set up Jenkins to respond to Github commits and call Ansible to automatically spawn and
configure our test servers, and we have optimized our
testing speed by using pre-made Docker images. n
Chris studied Computer Engineering and Informatics at the University of Patras, Greece and is now a Backend Developer, CI/QA
Engineer at Mist.io
Reprinted with permission of the original author. First appeared in hn.my/mist (mist.io)
26 PROGRAMMING
module.exports = function(grunt) {
grunt.initConfig({
jshint: {
options: {
jshintrc: '.jshintrc'
},
all: [
'Gruntfile.js',
'asselts/js/*.js'
]
}
...
coffee =
concat =
uglify =
imagemin
require('gulp-coffee');
require('gulp-concat');
require('gulp-uglify');
= require('gulp-imagemin');
var paths = {
scripts: ['client/js/**/*.coffee', '!client/
external/**/*.coffee'],
images: 'client/img/**/*'
};
gulp.task('scripts', function() {
// Minify and copy all JavaScript (except
vendor scripts)
return gulp.src(paths.scripts)
.pipe(coffee())
.pipe(uglify())
.pipe(concat('all.min.js'))
.pipe(gulp.dest('build/js'));
});
27
28 PROGRAMMING
Primitives
Targets
Tasks
Make, no more.
If youre setting out to make
another build tool, think about
what benefit youre going to provide over the existing ones. Adding
yet another custom DSL and grammar isnt going to really solve any
problem.
29
Functions
Safer Scripting
ExtractBashComments() {
egrep "^#"
}
cat myscript.sh | ExtractBashComments | wc
comments=$(ExtractBashComments < myscript.sh)
30 PROGRAMMING
Variable Annotations
==
=~
-n
-z
-eq
-ne
single bracket
[ "${name}" \> "a" -o ${name} \< "m" ]
double brackets
[[ "${name}" > "a" && "${name}" < "m"
]]
Regular Expressions/Globbing
x=5
x=6
readonly x
x=7
# failure
t="abc123"
[[ "$t" == abc* ]]
#
[[ "$t" == "abc*" ]] #
[[ "$t" =~ [abc]+[123]+
[[ "$t" =~ "abc*" ]] #
true (globbing)
false (literal matching)
] # true
# (regular expression)
false (literal matching)
# true
Operator Meaning
logical or (double brackets only)
logical and (double brackets only)
string comparison (no escaping necessary
within double brackets)
-lt
numerical comparison
=
string matching with globbing
||
&&
<
31
String Manipulation
Basics
f="path1/path2/file.ext"
len="${#f}" # = 20 (string length)
# slicing: ${<var>:<start>} or
${<var>:<start>:<length>}
slice1="${f:6}" # = "path2/file.ext"
slice2="${f:6:5}" # = "path2"
slice3="${f: -8}" # = "file.ext"(Note: space
before "-")
pos=6
len=5
slice4="${f:${pos}:${len}}" # = "path2"
32 PROGRAMMING
Built-In Variables
For reference
$0 name of the script
$n positional parameters to script/function
$$ PID of the script
$! PID of the last command executed (and run in the
background)
$? exit status of the last command (${PIPESTATUS}
for pipelined commands)
$# number of parameters to script/function
$@ all parameters to script/function (sees arguments as
separate word)
$* all parameters to script/function (sees arguments as
single word)
Note
$* is rarely the right choice
$@ handles empty parameter list and white-space
within parameters correctly
$@ should usually be quoted like so "$@"
Debugging
To perform a syntax check/dry run of your bash script,
run:
bash -n myscript.sh
You do not have much need for invoking other programs or pipelining them
References
33
SPECIAL
quite
adventurous for me and
quite a bit more complicated than some of the other projects Ive done with my G-Shock
watches. It involves doing some
pretty nasty things to the screen
of a naked G-Shock, so if youre
faint-hearted this is probably not
the ideal DIY starter project for
you. If youre still reading this and
desperately wanting to try reversing
the display of one of your digital
watches, read on!
Im going to be taking my plain
Casio G-Shock DW-5600 and
converting the regular display into
a negative one with the use of
some self-adhesive polarizing film.
I bought mine from Polarization.
com in Texas. I ordered the thinnest
self-adhesive film they had in a relatively small size, part name: Linear
Polarizer w/adhesive PFA.
Ok, on to the project. First let
me show you some of the tools you
might like to have ready for this.
his project was
34 SPECIAL
Plastic tweezers
Take off the straps so that you can remove the back
cover and so that they wont get in the way while you
are working on the body of the watch. I like to use
my nifty little Bergeon spring bar tool that is designed
specifically for this.
35
36 SPECIAL
37
The final step is to reassemble the whole thing. Carefully put the whole module back into the watch casing,
making sure it is seated down. I find that I nearly
always have to use my tiny screwdriver to hold in the
metal connectors where the buttons are in order to get
a module back in.
Replace the rubber spacer making sure that the
protruding metal contacts show through. Then replace
the metal case back and four screws. Im not showing
pictures of these steps because most of you know how
to do this and if you dont simply read through the
steps above that describe how to take the module out.
Reprinted with permission of the original author. First appeared in hn.my/negative (briangreen.net)
38 SPECIAL
resolution.
You want to let go of the fear of
fucking up, of it not being perfect,
of what other people think, of
things that probably wont ever
happen, and just crank that stuff
out, baby.
39
Dashboards
StatsD
Happiness
*Hosted Graphites mantis shrimp / eagle breeding program has been unsuccessful thus far
40 SPECIAL