summaryrefslogtreecommitdiff
path: root/posts/2013-08-11-hacking-the-wavedrum.skr
blob: 5751f8568f9e713ad7336b5a063290457fc118a5 (about) (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
(post
 :title "Hacking the Wavedrum"
 :date (string->date* "2013-08-11 00:00")
 :tags '("DIY"
         "planet-fsfe-en"
         "electronics"
         "music"
         "hacking"
         "wavedrum")

 (p [The Wavedrum Oriental is a wonderful electronic instrument.
     Unlike an electronic drum set or drum machines with touch
     sensitive pads, this drum synthesizer’s sensors don’t merely
     trigger samples.  The sensors rather behave like microphones or
     the pickup in an electric guitar; the signals of the four
     sensors—one sensor for the drum head, one sensor on the left and
     another on the right of the metal rim, and a pressure sensor in
     the centre of the drum—are used to control drum synthesizer
     algorithms whose output can be mixed with PCM samples.  As a
     result, the instrument feels a lot like a real drum, a feat that
     cannot easily be achieved with devices that use simple
     velocity-sensitive sample triggers.])

 (p [For all its magic, the Wavedrum also has a number of flaws.  Most
     prominently, all editing is done through five buttons and an
     endless rotary encoder.  What parameters can be selected by the
     buttons and thus manipulated by the encoder depends entirely on
     context; one button is used to jump to the next parameter page;
     simultaneously pressing a pair of buttons switches between the
     two edit modes (why two?), “global” mode and “live” mode; as one
     navigates through this confusing environment, a three-character
     seven-segment display conjures up magic strings that occasionally
     resemble abbreviations of what appear to be parameter names.
     Without a copy of the manual lying next to the device, any
     attempt at deciphering the cryptic three-character hints is
     doomed to fail.])

 (p [Another painful flaw is the lack of connectivity.  There is no
     way to export these precious custom programmes that were edited
     with so much difficulty.  There is no way to back up the
     programmes, nor can one share programmes with another Wavedrum.
     When the device dies, all custom patches go down with it.  Or so
     it seemed.])


 (h2 [A look inside the Wavedrum])

 (p [Not really knowing what to look for I opened up the Wavedrum in
     the hopes of finding ,(em [something]) that would allow me to
     extend the feature set of the instrument.  I first took off the
     control panel.  Only three screws have to be loosened to lift the
     front panel and look underneath.  The PCB, however, does not
     offer much of interest.])

 (p [Much more interesting is the main board which is located right
     underneath the drum head.  Removing the rim and the drum head
     apparently is a permitted activity which does not void the
     warranty—the manual includes instructions on how to change the
     drum head.])

 (wide-img "2013/wavedrum-opened.jpg"
           "After removing the rim and the drum head")

 (p [Once the rim and drum head are out of the way, one can already
     see much of the main board, but access is denied by a transparent
     plastic disk.  In my instrument the plastic disk was only screwed
     to two posts although there are holes for seven screws.  The
     heads of the two screws are covered with adhesive pads that can
     easily be removed to undo the screws.  (Don’t worry, the glue on
     the pads is strong enough to put them back on when you’re done.)])

 (list 'warning [,(strong [Warning:]) if you are following these
  instructions, at this point, I believe, you might be voiding your
  warranty.  If you’re careful and you don’t tell anyone, nobody
  should ever notice.  Note that I cannot be made responsible for any
  damage to your device that may result from following these
  instructions.])

 (p [With this warning out of the way, let’s move on.])

 (p [The main board is very tidy making it easy to understand what’s
     going on.  The densely packed section on the right appears to be
     power supply and rim sensor amplification logic.  On the left you
     can see two medium-sized chips, a bulky capacitor and something
     covered with a black, textured tape.  The two chips are RAM
     (ESMT) and flash memory (cFeon), respectively.  The big capacitor
     buffers the power for the two memory chips and the massive DSP on
     the back of the board.  The back side of the board is rather
     boring as it really only holds the DSP chip (ADSP-21375 by Analog
     Devices).  The board has a somewhat unusually great number of
     test pads (most of which are connected to ground, used for
     automated testing) and quite a few connector pads, possibly
     allowing a hardware debugger to be connected to debug the DSP’s
     firmware ,(em [in situ]).])

 (wide-img "2013/wavedrum-mainboard.jpg"
           "The mainboard of the Wavedrum Oriental")


 (h2 [The treasure trove])

 (p [What is underneath that black tape on the front, though?  This is
     where things get really interesting (well, to people like me, at
     least).  As I carefully removed the tape I was pleasently
     surprised to see a micro SD card reader underneath.  The card is
     locked to the reading interface to make sure it stays in place
     during operation.  Unlock it by shifting the metal brace to the
     right whereupon it can be lifted.])

 (figure "2013/wavedrum-card-tape.jpg" "The taped-over SD card")

 (p [The 2GB micro SD card is a standard card formatted with a FAT32
     filesystem, making it possible to read it out with a standard
     card reader.  My netbook has a built-in SD card reader only, so I
     first needed to buy an adapter to connect the micro SD card.
     This reader is a little weird.  It seems that the adapter must be
     in the reader slot on boot or the micro SD card won’t be
     recognised.  (If you’re unsure whether the card is recognised by
     your system check the output of ,(code [dmesg]).)  Eventually,
     the card was recognised as ,(code [/dev/sdb1]).  (,(code
     [/dev/sdb]) is the SD card reader device itself.)  As this is my
     only Wavedrum and I intend to use it for years to come I decided
     to be especially careful this time and only operate on a ,(em
     [copy]) of the card.  The Wavedrum’s card reader is perfectly
     capable of reading 8GB micro SD HC cards, so if you want to play
     with the data on the card I recommend mirroring the original card
     image onto whatever micro SD card you have at your disposal and
     play with that instead of the original card.  To create a block
     level copy of the card I ,(em [did not]) mount the filesystem and
     simply executed the following command:])

 (pre (code [dd if=/dev/sdb1 of=wavedrum.img]))

 (p [This instructs ,(code [dd]) to copy all blocks from the input
     device file (,(code [if]), i.e. ,(code [/dev/sdb1])) to the
     output file (,(code [of])) of the name ,(code [wavedrum.img]).
     Dependent on the number of disks on your system, the input device
     file may have a different name.  Check the output of ,(code
     [dmesg | tail]) as you connect the card reader to see which
     device node is created for the micro SD card. Note that this
     blindly copies ,(em [everything]) on the micro SD card, not just
     files that are available through the FAT32 filesystem.  Hence,
     the size of the image is quite a bit larger than the sum of all
     files on the mounted image (502,145,536 bytes vs 234,479,878
     bytes).])

 (p [Before continuing, please put the micro SD card back into the
     Wavedrum’s card reader and lock it to prevent it from being
     damaged (things can get messy, you know).  Going forward, we only
     need to mount the image to access the data stored on the card.
     Run the following as root to mount the card image as a read-only
     filesystem:])

 (pre (code [mkdir wavedrum
mount -o loop,ro wavedrum.img wavedrum/]))

 (p [Let’s take a look at the files on the card:])

 '(ul (@ (class "tree"))
      (li (span (@ (class "NORM")) "/" ))
      (li "├── (  16)  "     (span (@ (class "EXEC")) "CALIB.BOR"))
      (li "├── ( 16K)  "     (span (@ (class "DIR"))  "Factory"))
      (li "│   ├── (192K)  " (span (@ (class "EXEC")) "F_INFO.BOR"))
      (li "│   ├── ( 57K)  " (span (@ (class "EXEC")) "F_INST_H.BOR"))
      (li "│   ├── ( 57K)  " (span (@ (class "EXEC")) "F_INST_R.BOR"))
      (li "│   ├── ( 16K)  " (span (@ (class "EXEC")) "F_PROG.BOR"))
      (li "│   └── (  88)  " (span (@ (class "EXEC")) "F_USER.BOR"))
      (li "├── ( 57K)  "     (span (@ (class "EXEC")) "INST_H.BOR"))
      (li "├── ( 57K)  "     (span (@ (class "EXEC")) "INST_R.BOR"))
      (li "├── ( 16K)  "     (span (@ (class "DIR"))  "LOOP"))
      (li "│   ├── (744K)  " (span (@ (class "EXEC")) "LOOP0001.BIN"))
      (li "│   ├── (402K)  " (span (@ (class "EXEC")) "LOOP0002.BIN"))
      (li "│   ├── (750K)  " (span (@ (class "EXEC")) "LOOP0003.BIN"))
      (li "...")
      (li "│   ├── (173K)  " (span (@ (class "EXEC")) "LOOP0138.BIN"))
      (li "│   ├── (173K)  " (span (@ (class "EXEC")) "LOOP0139.BIN"))
      (li "│   └── (234K)  " (span (@ (class "EXEC")) "LOOP0140.BIN"))
      (li "├── ( 16K)  "     (span (@ (class "EXEC")) "PRE_PROG.BOR"))
      (li "├── ( 16K)  "     (span (@ (class "DIR"))  "SYSTEM"))
      (li "│   ├── (  16)  " (span (@ (class "EXEC")) "VERSION.INF"))
      (li "│   ├── (1.0M)  " (span (@ (class "EXEC")) "WDORM202.BIN"))
      (li "│   └── (8.0K)  " (span (@ (class "EXEC")) "WDORS110.BIN"))
      (li "├── (  88)  "     (span (@ (class "EXEC")) "USER.BOR"))
      (li "├── (157M)  "     (span (@ (class "EXEC")) "WD2_DATA.BOR"))
      (li "├── (192K)  "     (span (@ (class "EXEC")) "WD2_INFO.BOR"))
      (li "└── ( 16K)  "     (span (@ (class "EXEC")) "WD2_PROG.BOR")))

 (p [The files in the ,(code [Factory]) directory contain
     initialisation data.  When a factory reset is performed, the
     customised versions of these files in the root directory are
     overwritten with the versions contained in the ,(code [Factory])
     directory.  All initial programmes that come with the Wavedrum
     are stored in ,(code [Factory/F_PROG.BOR]); once programmes have
     been edited ,(code [WD2_PROG.BOR]) in the root directory will
     differ from ,(code [Factory/F_PROG.BOR]).  (More about the nature
     of these differences later.)  ,(code [PRE_PROG.BOR]) is the same
     as ,(code [Factory/F_PROG.BOR]) and is probably used to make the
     original factory presets available in addition to custom
     programmes, starting at position ,(code [P.00]), the programme
     slot after ,(code [149]).])

 (p [The initial mapping of presets to any of the 12 slots (3 banks
     with 4 slots each) is stored in ,(code [Factory/F_USER.BOR]).
     Initially, ,(code [USER.BOR]) in the root directory will be
     identical to this file.  The format of this file is rather
     simple:])

 (pre (code [00000000 |  00 00 00 64 00 00 00 67  00 00 00 7b 00 00 00 6c
00000010 |  00 00 00 65 00 00 00 68  00 00 00 71 00 00 00 7a
00000020 |  00 00 00 84 00 00 00 8c  00 00 00 8b 00 00 00 95
00000030 |  00 00 00 00 00 00 00 00  00 00 00 75 00 00 00 26
00000040 |  00 00 00 07 00 00 00 14  00 00 00 07 00 00 00 14
00000050 |  00 00 00 05 00 00 00 64]))

 (p [Every 8 digit block (4 byte) is used for one slot.  We can see
     that the first slot in bank A is set to programme 100 (0x64 hex),
     the second to programme 103 (0x67 hex) and so on.  As the
     Wavedrum only allows for 12 slots to store programme identifiers,
     only the first 48 bytes are used for programmes.  The remaining
     40 bytes (starting at 0x30) are used for global parameters that
     can be adjusted in “global” editing mode.  The global parameters
     are stored in this order:])

 (ul
  (li [delay pan])
  (li [aux input level])
  (li [loop phrase select])
  (li [loop play mode (off=38)])
  (li [head sensor threshold])
  (li [head sensor sensitivity])
  (li [rim sensor threshold])
  (li [rim sensor sensitivity])
  (li [pressure sensor threshold])
  (li [pressure maximum]))

 (p [I don’t know yet what purpose ,(code [F_INST_H.BOR]) and ,(code
     [F_INST_R.BOR]) serve, but it is clear that the former relates to
     settings for the drum head while the latter contains similar
     settings for the rim.  Even after editing a few programmes,
     ,(code [INST_H.BOR]) and ,(code [INST_R.BOR]) in the root
     directory were still identical to their counterparts in the
     ,(code [Factory]) directory.])

 (p [The ,(code [CALIB.BOR]) appears to contain calibration
     information for the head and rim sensors.  This is different from
     the calibration performed by adjusting the four global paramaters
     for sensor threshold and sensitivity.  I have not been able to
     edit these settings through the Wavedrum so far, so these
     probably are factory settings.])


 (h2 [Audio data])

 (p [All files in the ,(code [LOOP]) directory as well as ,(code
     [WD2_DATA.BOR]) contain raw audio data.  Unfortunately, I haven’t
     quite figured out the format yet, but you can listen to the
     clearly recognisable loop patterns with ,(code [play]) (part of
     the ,(ref "http://sox.sourceforge.net" "SoX") applications):])

 (pre (code [find LOOP -name "*.BIN" -print |\\
  xargs -I XXX \\
  play -t raw -r 48k -b 16 -e signed-integer -c 1 XXX]))

 (p [Obviously, this isn’t quite correct.  I’m interpreting every 16
     bits as a sample in signed integer format, but the sound is
     distorted and far from the realistic instrument sound when
     playing back the loops through the Wavedrum.])

 (p [All loops start with this 44 byte long header:])

 (pre (code [04 dc 10 d3 uU vV 5W 95  01 d4 00 d0 30 f8 22 b5
46 95 56 95 57 95 57 95  d6 2e 56 95 56 e2 57 95
54 95 46 95 32 f4 22 f4  xX yY 5W 95]))

 (p [With a few exceptions (namely 0009, 0025, 0027, 0030, 0033, 0036,
     0049, 0054, 0064, 0082, 0091, 0103, 0104, 0107, 0108, 0127, 0128,
     0129, 0130, 0131, 0132, 0135), vV equals yY in most loops. It
     seems that loops with the same number of bytes have the exact
     same numbers for uU, vV, W, xX, and yY.  This is especially
     apparent in the loops 0127 to 0132 (inclusive), which are all
     192,010 bytes long and all have the values 54:7b:54 for uU:vV:5W
     and 88:78:54 for xX:yY:5W.])

 (p [Clearly, more work is required to figure out the complete format
     of these loop files.  Once this is understood we could use custom
     loops with the Wavedrum.])

 (p [The raw audio data in ,(code [WD2_DATA.BOR]) suffers from the
     same problems.  Although the data can be interpreted as raw
     audio, the sound is distorted and playback is unnaturally fast.])


 (h2 [System files])

 (p [I don’t know what ,(code [SYSTEM/WDORS110.BIN]) is used for.  The
     only useful string contained in the file is “BOOTABLE”.  Your
     guess is as good as mine as to what it does.])

 (p [,(code [SYSTEM/VERSION.INF]) is only 16 bytes short and pretty
     boring as it contains just what the name implies: version
     numbers.])

 (pre (code [02 02 01 10 02 02 00 00  57 44 4f 52 00 00 00 00]))

 (p [This string of numbers is interpreted as follows: firmware
     version 2.02, sub-version 1.10, data version 2.02 (followed by
     two empty bytes); 57 44 4f 52 (hex for “WDOR”) stands for
     “Wavedrum Oriental” (followed by four empty bytes).  You can have
     the Wavedrum display all its version numbers by pressing the
     button labelled “Global” when powering on the device. Note that
     the file name ,(code [WDORS110.BIN]) references the version
     number 1.10, while ,(code [WDORM202.BIN]) references the firmware
     version number 2.02.])

 (p [,(code [SYSTEM/WDORM202.BIN]) contains the firmware of the
     Wavedrum Oriental.  There are many interesting strings and binary
     patterns in the file, but I’m still a long way from ,(em
     [understanding]) how it works.  To view the strings with the
     ,(code [strings]) command, you have to specify the encoding as
     32-bit little endian:])

 (pre (code [strings --encoding L SYSTEM/WDORM202.BIN]))

 (p [Some of the strings embedded in the firmware are file names, some
     of which are not available on the micro SD card.  This includes
     the following files: SYS00000.BIN, SYS00100.BIN,
     SYSTEM/WDORS100.BIN, SYSTEM/WDX_M100.BIN, SYSTEM/WDX_S100.BIN,
     and SUBXXXXX.BIN (a pattern?).])


 (h2 [The programme format])

 (p [Looking at the hexdump of the file ,(code [WD2_PROG.BOR]) which
     holds all custom presets, I couldn’t find any obvious patterns in
     the file, so I resorted to editing a single programme, setting
     particular consecutive parameters to easily recognisable
     sequences of values (such as 100, 99, 98, and 97 for hd1, hd2,
     hd3, and hd4) and locating the changes in the hexdump.])

 (wide-img "2013/wavedrum-diff.png"
           "Analysing the programme format by changing values and looking at the differences")

 (p [This procedure has allowed me to figure out in what order the
     parameters are stored in the file.  Each programme is exactly 54
     16-bit words long; each parameter takes up exactly 16 bits.
     Negative values are stored in ,(ref
     "https://en.wikipedia.org/wiki/Two%27s_complement" "two’s
     complement") format (e.g. negative six is stored as 0xFFFA).  The
     file is exactly 16200 bytes long which is just enough to hold 150
     custom programmes, each taking up 108 bytes.])

 (p [I’m currently writing a Haskell library to parse / build
     progammes and parameters.  The code is available for ,(ref
     "https://www.fsf.org/about/what-is-free-software" "free") ,(ref
     "http://git.elephly.net/wavedrum/wavedrum-lib.git" "here") under
     the ,(ref "https://gnu.org/licenses/gpl.html" "GNU GPLv3").])

 (p [The parameters are stored in this order:])

 (table :align '("right")
        :headers '("identifier" "mode" "target" "name")
        :rows
        '(("07.1" "Edit 1" "head algorithm"      "Pressure curve")
          ("type" "Edit 2" "–"                   "Pre EQ")
          ("01.1" "Edit 1" "head algorithm"      "Tune")
          ("02.1" "Edit 1" "head algorithm"      "Decay")
          ("03.1" "Edit 1" "head algorithm"      "Level")
          ("04.1" "Edit 1" "head algorithm"      "Pan")
          ("05.1" "Edit 1" "head algorithm"      "Algorithm select")
          ("hd.1" "Edit 2" "head algorithm"      "Algorithm parameter 1")
          ("hd.2" "Edit 2" "head algorithm"      "Algorithm parameter 2")
          ("hd.3" "Edit 2" "head algorithm"      "Algorithm parameter 3")
          ("hd.4" "Edit 2" "head algorithm"      "Algorithm parameter 4")
          ("hd.5" "Edit 2" "head algorithm"      "Algorithm parameter 5")
          ("hd.6" "Edit 2" "head algorithm"      "Algorithm parameter 6")
          ("hd.7" "Edit 2" "head algorithm"      "Algorithm parameter 7")
          ("hd.8" "Edit 2" "head algorithm"      "Algorithm parameter 8")
          ("01.3" "Edit 1" "rim algorithm"       "Tune")
          ("02.3" "Edit 1" "rim algorithm"       "Decay")
          ("03.3" "Edit 1" "rim algorithm"       "Level")
          ("04.3" "Edit 1" "rim algorithm"       "Pan")
          ("05.3" "Edit 1" "rim algorithm"       "Algorithm select")
          ("rm.1" "Edit 2" "rim algorithm"       "Algorithm parameter 1")
          ("rm.2" "Edit 2" "rim algorithm"       "Algorithm parameter 2")
          ("rm.3" "Edit 2" "rim algorithm"       "Algorithm parameter 3")
          ("rm.4" "Edit 2" "rim algorithm"       "Algorithm parameter 4")
          ("rm.5" "Edit 2" "rim algorithm"       "Algorithm parameter 5")
          ("rm.6" "Edit 2" "rim algorithm"       "Algorithm parameter 6")
          ("rm.7" "Edit 2" "rim algorithm"       "Algorithm parameter 7")
          ("rm.8" "Edit 2" "rim algorithm"       "Algorithm parameter 8")
          ("01.2" "Edit 1" "head PCM instrument" "Tune")
          ("02.2" "Edit 1" "head PCM instrument" "Decay")
          ("03.2" "Edit 1" "head PCM instrument" "Level")
          ("04.2" "Edit 1" "head PCM instrument" "Pan")
          ("05.2" "Edit 1" "head PCM instrument" "PCM instrument select")
          ("06.2" "Edit 1" "head PCM instrument" "Velocity curve")
          ("07.2" "Edit 1" "head PCM instrument" "Pressure curve")
          ("08.2" "Edit 1" "head PCM instrument" "Pressure tune")
          ("09.2" "Edit 1" "head PCM instrument" "Pressure decay")
          ("01.4" "Edit 1" "rim PCM instrument"  "Tune")
          ("02.4" "Edit 1" "rim PCM instrument"  "Decay")
          ("03.4" "Edit 1" "rim PCM instrument"  "Level")
          ("04.4" "Edit 1" "rim PCM instrument"  "Pan")
          ("05.4" "Edit 1" "rim PCM instrument"  "PCM instrument select")
          ("06.4" "Edit 1" "rim PCM instrument"  "Velocity curve")
          ("07.4" "Edit 1" "rim PCM instrument"  "Pressure curve")
          ("08.4" "Edit 1" "rim PCM instrument"  "Pressure tune")
          ("09.4" "Edit 1" "rim PCM instrument"  "Pressure decay")
          ("10.1" "Edit 1" "–"                   "Reverb type")
          ("10.2" "Edit 1" "–"                   "Reverb effect level")
          ("10.3" "Edit 1" "–"                   "Reverb decay time")
          ("10.4" "Edit 1" "–"                   "Reverb frequency damping")
          ("11.3" "Edit 1" "–"                   "Delay feedback")
          ("11.2" "Edit 1" "–"                   "Delay effect level")
          ("11.1" "Edit 1" "–"                   "Delay time")
          ("11.4" "Edit 1" "–"                   "Delay frequency damping")))


 (h2 [Thanks])

 (p [The following tools have proven indispensable in the analysis:])

 (ul
  (li [,(ref "http://www.isthe.com/chongo/tech/comp/calc/" "calc"), a
       calculator for the command line supporting hexadecimal
       representations of numbers (both input and output using the
       ,(code [base]) and ,(code [base2]) functions)])

  (li [,(ref "http://www.cjmweb.net/vbindiff/" "vbindiff"), a tool to
       visualise differences between two binary files with a
       split-screen hexadecimal display])

  (li [,(ref "https://wiki.gnome.org/Ghex" "ghex"), a simple
       hexadecimal editor supporting pattern search and highlighting]))


 (h2 [Call for help])

 (p [If you own an earlier model of the Wavedrum or the latest
     Wavedrum Global Edition, I would be ,(em [very]) happy if you
     could send me a block-level copy of the SD card (see above for
     instructions).  This would allow me to understand the firmware
     better and maybe even make it possible to upgrade an older
     Wavedrum to the latest version (taking into account possible
     hardware differences, such as differing memory size).])

 (p [Please send a link to the micro SD card image to ,(email
     "rekado@elephly.net").])

 (p [Read ,(ref "/tags/wavedrum.html" "more posts about the Wavedrum
     here").]))