Thinking on what and how to do an Arduino specific 1-bit engine.

Feature set does not seem too important. It could be something like a scaled up Tritone or Phaser, with more channels, with ability to play tone or noise. It needs to support things that are difficult in ZX engines, such as clean vibrato, portamento, and maybe 'instruments' (fx tables) in general.

There is a major issue that stops me at the moment, the authoring tools. We can't really make music for engines with any complex instruments system using an XM converter. We can't really emulate Arduino to hear authentic sound while making music - well, we could make a whole dedicated tracker with AVR emulator, but that's a ton of work just to start. An VSTi can be made that will generate more or less similar sound, but this would kind of defeat the whole point, because such VSTi would be self-sufficient and the sound won't fully match.

One idea is to make MIDI interface, just as people asked, and control the Arduino with a MIDI-enabled DAW, to be able to use existing tools for composing and hear fully authentic sound in the process. This, however, requires to have the hardware and two MIDI interfaces (on the PC and Arduino's MIDI Shield, which is barely obtainable here and costs more than Arduino, by the way).

Yet another idea is a VSTi again, but this time one that would just emulate AVR and loaded firmware with the engine. Again, lots of work, but at least the whole Arduino thing won't be redundant in this case.

Any MIDI-related approach also makes it difficult to create instruments, it has to be done with SysEx and a custom tool. Or just limit a composer to use pre-defined patches.

Thanks, PROTODOME. That's the point of this project, to make the 1-bit music accessible for more people, with more modern tools and hardware.

Yeah, I think the next logical step would be creating all-new custom engine that would actually play around strengths of the platform. Have no idea what it is could be just yet, though. Maybe a super scaled Phaser or Tritone like one, to begin with (authoring tools should be considered too). One thing that would be easy to do on Arduino is the clean vibrato and portamento that ZX engines usually don't have.

Yes. That was the most challenging engine to port, considering its design and the need to re-implement it using timer interrupts, but also the most interesting.

I used the same ideas as for Tritone engine, but the interrupts train system is more complicated. Just like the original, there is the main loop running at the sample rate, consisting two equal halves for each channel. When 8-bit counter overruns, a pulse of a fixed duration is generated. However, duty cycle of the pulse (the amount of time for 1 and 0 in that pulse) changes depending on the sustain parameter, creating pseudo volume decay. The pulse is generated using timer interrupts as well, setting up the timer to desired values. To get enough timer resolution (the pulses can be pretty long compared to the sample rate), I had to use prescaler 32 rather than 8, like it was in all previous ports, which affected to the sample frequency, making it a bit off.

The drums were done just like in Tritone port too, using pulse coded samples of the original drums.

I think that's the last port in the line, as all other engines use one of the techiniques of already ported engines, nothing more to explore.

405

(15 replies, posted in Other Platforms)

No, Linux double boot is not an option. Unfortunately my notebook that is normally used for such things broke, and I don't want to mess with my main working PC having no backup working machine.

Interestingly, I noticed that while the COM monitor is active, the resetting stops, so it became a bit easier to debug.


Anyways. I think I found a fix for the player hanging. That's likely related to the way I was invoking the drums - by changing the interrupt handler pointer from the main thread. But that's AVR8, so this operation is not atom - it changes two bytes one by another, and there is a good chance (considering the high sample rate) that timer interrupt will occur in the middle of the change, calling the half-changed address of the handler function. I tried to fix it by changing the handler only in the timer interrupt handler, where it is guaranteed that another interrupt won't occur. Re-attached the code to the first post, please test.

406

(15 replies, posted in Other Platforms)

I also found and read that article about phone software interferring with Arduino. Actually tried to remove all drivers that use COM mode, figured out which ones they are using USBDeview. Nothing changed at all - unstable upload, constant resetting. Guess the only option that is left is to disable the auto reset indeed, will try it later.

407

(15 replies, posted in Other Platforms)

Found the power supply. Didn't help, the Arduino keeps resetting after a few seconds while it connected to the PC. So that's not a power issue, likely a software one (something accesses the USB port, causing resets).

408

(15 replies, posted in Other Platforms)

Thanks for the information, I'll try to figure out what's going wrong. It has to be something with the code. Maybe the interrupt train system is not very reliable in some case. Although it is very determined and set to exact same state at start, so it expected to fail always at the same spot.

My Arduino works well with the external 5V power source plugged into USB. The problem is that I need to have it hooked up to PC in order to debug. Haven't found a suitable 9V adapter just yet ('minus outside' are rarity here, don't have spare ones to rewire either), hopefully it will fix the issue.

409

(15 replies, posted in Other Platforms)

That's weird, but I can't really test it - neither I know how the songs supposed to sound, nor my Arduino works stable while it connected to the PC, it randomly resets after a few seconds, and I haven't found a fix for this yet. If you will be able to provide a test where it will be easy to spot the issue, like really hanging on the first notes or something, I'll be able to examine and fix the problem (none of your tests stopped too soon for me, but I wasn't able to listen them entirely).

410

(15 replies, posted in Other Platforms)

Thanks for report, but you forgot to attach the files.

Regarding the slower tempo, no, Arduino can handle crazy speeds, that's not the issue. That's seem to be just a misimplentation of the thread sync (row counters in beeper engines may work in tricky ways). Thinking how to fix it now.

Edit: made the tempo fix and replaced the test song with yours. The file attach replaced, please re-download.

411

(15 replies, posted in Other Platforms)

Now we're getting into something tricky and less straightforward that is not transferring to the Arduino and C well. That's the Tritone engine.

The main feature of the engine is the uneven volume balance, giving virtual volume control without the usual PWM'ish artifacts. It was achieved by using the standard interleaving technique, but assigning time slots of different length for each of the channels. I.e. longest slot gives loudest sound, shortest slot gives quitest sound. That's easy to achieve with the timed code, but in C on Arduino we don't have such luxury.

It (probably) can't be done with just a single fixed-frequency timer interrupt at a sample rate as high as half of the shortest slot (to be able to align output sequence to 2,3,4 samples to keep the original volume balance) - that would give over 200 KHz, and that's likely too much, just the interrupt call/return will hog most of CPU time.

A better approach that I come up with is to set a train of interrupt handlers, each sets up the timer to another value. The hardware interrupt handler function only contains call of the actual handler by a function pointer. There is three actual interrupt handling functions that gets called with the timer interrupt in a circular sequence. Each one sets up the next timer value, outputs current slot, calculates whathever is needed, and sets up the pointer to the next handler function. This gives steady sample rate just like the original Tritone had (actually 22988 Hz, due to the timer prescaler, which is not far from the original 22875 Hz), with proper interleaving slots aligment.

Drums were another tricky part. As the original code sure does not translate to the timer interrupt-driven approach well, I decided to do it lazy way this time - as 1-bit samples of the original sounds. There is fourth interrupt handling function that sets up the timer to doubled sample rate (45977 Hz) and plays sample while keeping the sync with parser thread. Once sample is done playing, the handler reverts to the tone synth handlers loop. Samples of the drums were captured from Unreal Speccy at 96 KHz, downsampled to the desired rate, amplified a lot to get hard clipping, so they're mostly max or zero volume, converted to 8-bit, then processed with a custom tool written in Python. To save Flash memory I used a RLE-like packing that only records how many samples there were between changes 0 to 1 or vice versa (I think that's what Speak Easy uses on ZX Spectrum), that's what the tool doing, besides of finding individual drum shots in a single WAV file.

Yet again, you can compile Tritone songs for the Arduino port with Beepola, exporting them as BIN without player. The test song is by garvalf - 'Bourrasques'.

412

(7 replies, posted in Other Platforms)

utz, yeah, I believe a lot of very impressive 1-bit stuff can be pulled of a bare AVR programmed in assembly. Really worth to give it a shot eventually.

garvalf, well, a MIDI controlled synth it totally doable, but it is much more work compared to these simple players, the sound synthesis part of the code would be just a small part of it. I'm thinking about it time to time, but have no plans to do it in anytime soon. It is difficult enough already to find time to invest into these smaller projects.

413

(7 replies, posted in Other Platforms)

Guess 10x sample rate is just too much for the 16-bit adders. Can be tweaked, if neccessary. For example, 5x sample rate:

#define SAMPLE_RATE           (3500000/80)
#define MUSIC_FRAME           64*5
#define DRUM_DOWNSAMPLE      5

414

(7 replies, posted in Other Platforms)

This time I decided to give QChan a go. This is another kind of engine from the previous two, one that uses actual variable pulse width. So that's a new thing to try. I learned about a trick that allows to generate one-shot pulses using the Fast PWM mode on AVR in advance, but turned out that it is simply not needed, as naive approach with just delayMicroseconds works very well.

I added an enhanced mode just for fun. Original QChan's loop runs at 8663 Hz. Turns out that Arduino has no problem to run it at 86630 Hz, with code written all in C! This improves the sound quality quite a bit.

Again, the clicky drums aren't 100% recreated, just an approximation of the sound is implemented, because they were using ever-changing sample rate, and that's does not fit the simple timer interrupt-driven design well.

Again, you can just compile QChan modules in Beepola in order to play them with this port.


The music in the test is MovieMovies1's 'cant think of a music either'.

By the way, if someone don't know, Beepola is capable to extract music back from tap files that were generated with it. This may come handy when you need to get music for testing and need to recompile it to a different address or something. See File>Import.

Alright, I had enough of crappy sound coming from an old trasistor radio that has been used for testing for Octode and Phaser1 ports, which made quite difficult to judge if the sound is even correct. It became apparent that I need a better control, especially if something more complex or tricky than these engines considered, so I came up with a very simple schematics that may come handy if you need something like this too. Note that I'm not an EE, it may have mistakes in the design, but at least it something that works. It can be used to connect headphones (volume is not too bad even without amplifier), line input, or an amplifier.

R1 and C2 here are the low pass filter, the values calculated for ~16 KHz cutoff to avoid possible hiss artifacts that is typical for the 'interleaving' 1-bit enignes.

C1 is a coupling capacitor to block the DC offset. Potentiometer R2 allows to change the output level. The values for these were chosen just because I didn't have anything else. These together actually act as a high pass filter, but the cutoff is ~1.5 Hz, so it should not affect the sound. Volume range is good as well, smooth transition from zero to max.

http://randomflux.info/1bit/misc.php?action=pun_attachment&item=68&download=1


Also, some tech porn:

http://shiru.untergrund.net/temp1/ardui … _out_1.jpg
http://shiru.untergrund.net/temp1/ardui … _out_2.jpg

416

(10 replies, posted in Other Platforms)

I cooked up a more or less quality hookup schematics for headphones and line in, and indeed, no artifact/carrier noise.

417

(10 replies, posted in Other Platforms)

Sure, post about it whenever you think it is appropriate, maybe it'll inspire someone to do more 1-bit experiments. I personally don't have accounts on any Arduino-related resources.

Interesting, the carrier artifact is very prominent in these recordings. I wonder how it compares to the original ZX. There is hardware recording by Mister Beep himself, and it does not have any noticeable carrier, although he maybe EQed it a bit.

418

(37 replies, posted in Other Platforms)

The SPEAKER_BIT is a bit number in an I/O port, not just board pin number. To move the output to the pin 9, take a look to the Arduino pinout, find out which port and bit the desired pin is connected to (pin 9 is port B bit 1), and change SPEAKER_ defines accordingly. I.e. for pin 9 you need to set it like this:

#define SPEAKER_PORT          PORTB
#define SPEAKER_DDR           DDRB
#define SPEAKER_BIT           (1<<1)

I just needed to have best output performance, thus I avoided the (slower) Arduino functions which operate with the board pin numbers rather than ports/bits.

419

(10 replies, posted in Other Platforms)

Continuing the least imaginative way, here is second port of a ZX beeper engine to Arduino, in C.

This time it is an interleaving type engine. Porting approach is totally naive, just a timer interrupt at the bit output frequency, similar to the Octode implementation, but doubled because of the interleaving, with two alternating code paths. It easily works on its original sample rate of 23972 Hz (47945 Hz interrupts), and actually can work even at 44100 Hz (88200 Hz interrupts!). Didn't try higher rates because it was near the timer resolution limit with selected prescaler.

The most difficult part in the porting process was the Phaser1 format itself, it is quite tricky as you know. I chose the Beepola version, so you can just compile Phaser1 songs in Beepola and export them as BIN without player. Just don't forget that if you change the compile address in Beepola, you have to change the COMPILE_ADR in the C code too. Only the 'digital drums' version is implemented.

The song in the demo build is Mister Beep's Greetings to C64.


As for code tricks, nothing special here. The only tricky moment was catching the carry for 16-bit addition. It was done by checking if the value become lesser than it was before addition (normally should become greater), like that:

add hl,bc
jr c,...

n=acc;
acc+=add;
if(acc<n) ...

420

(37 replies, posted in Other Platforms)

The drums expected to be a bit different, as I decided to not recreate the original algorithm and try to add more 'weight' to them. Arduino being faster is because the drums don't affect the tempo anymore, they're completely transparent tempo-wise. Notes being off may be an issue with the Arudino code, need to investigate. Designed to be exact match for the tone channels.

421

(37 replies, posted in Other Platforms)

Well, there are many recommended simplest schematics like this or that, but these designed to work with (the hardware-generated) PWM audio, thus they're basically low pass filters, sometimes with the DC blocking capacitor as well. These should work just fine.

I, however, like to think about it as a 1-bit DAC. Just like Covox, but without the 7 other bits. I.e. a resistor as load, between the Arduino pin and ground, and another one between the pin and audio out (top two resistors on the linked schematics). Plus the optional capacitors to do low pass and DC block, if you will.

422

(37 replies, posted in Other Platforms)

Alright, I examined the most CPU time consuming line of the code and possible changes in it, and you know, somehow I chose the most optimal approach intuitively right off the bat. Any changes (trying to change the logic, order of condtion, playing around with temporary local register variables) only make it longer for an opcode or two. Here is the line and the code:

  if(cnt_load[0]) { --cnt_value[0]; if(cnt_value[0]==255) { output_state=SPEAKER_BIT; cnt_value[0]=cnt_load[0]; } }

 1ba:    20 91 08 01     lds    r18, 0x0108    ; 0x800108 <cnt_load>
 1be:    22 23           and    r18, r18
 1c0:    59 f0           breq    .+22         ; 0x1d8 <main+0xb4>
 1c2:    80 91 00 01     lds    r24, 0x0100    ; 0x800100 <_edata>
 1c6:    81 50           subi    r24, 0x01    ; 1
 1c8:    80 93 00 01     sts    0x0100, r24    ; 0x800100 <_edata>
 1cc:    8f 3f           cpi    r24, 0xFF    ; 255
 1ce:    21 f4           brne    .+8          ; 0x1d8 <main+0xb4>
 1d0:    90 93 10 01     sts    0x0110, r25    ; 0x800110 <output_state>
 1d4:    20 93 00 01     sts    0x0100, r18    ; 0x800100 <_edata>

Of course pure assembly code version can be made more efficient, by storing counters directly in registers and checking for carry rather than 0xff, but as for the C appraoch, not much can be improved.

423

(37 replies, posted in Other Platforms)

Here is the ELF file. I figured out how to find it, the worst thing about it was that the location is different with every build. So every time when you need to analyze the code, you have to manually copy/paste the path from the IDE verbose output. Also found the objdump (had it ony my PC, actually), and was able to see the assembly code, but I'll leave analyzing it and finding possible optimizations for later. At the first glance it seemed pretty effective, sans the tons of push/pops on the interrupt entry/leave.

Please note again, that's not my original song in the example. I can't recall who gave it to me and if he asked to not publish the XM module. So if anyone wants to provide his song to be distributed with this code (with proper credit), it would be cool.

424

(37 replies, posted in Other Platforms)

Yeah, I used the latest IDE, 1.8.1, and I have read that there is inconstince with the PROGMEM between versions.

I don't have problems other than that it takes ages to figure out and find everything. Like finding where the damn ELF file is located, where to get the avr-objdump without installing a pile of unneeded things, etc. I just need time to devote to this instead of more important things. Thanks, I'll try the avr-objdump sometime later.

425

(37 replies, posted in Other Platforms)

I don't know, but even so, I'm afraid it would only complicate things. Like, you would have to compile the library in one tool, then move to Arduino IDE and compile test from there. For end user, it would lack the main quality of the Arduino platform, being able to easily see and modify code. The only benefit of using Arduino that would remain is the on-board USB programming. Maybe there are reasonable use cases for this approach, though.

At the moment I don't really have ideas where to go next with Arduino. Maybe port some other engine(s), maybe explore possibilities with native engines. Sure nothing big that would take large assembly code parts. For 100-200 line pieces inline asm is tolerable.