Go Down

Topic: 1-bit music on the Arduino (Read 3942 times) previous topic - next topic

farvardin

Feb 14, 2017, 03:20 pm Last Edit: Feb 14, 2017, 03:27 pm by farvardin
What is 1-bit music?

It's music made from the speaker of a computer (no dedicated sound card), the state of the speaker can be 0 or 1. Generally it sounds very crude, like this: https://www.youtube.com/watch?v=1IOL4q5tDDQ (which can be cool too, this tune is so great)

But since the 80's it was possible to get a better sound through dedicated sound engines, made in assembly code, especially on the ZX Spectrum beeper (intro music for some game, it used so much CPU that is was not possible to have in-game music, remember, on ZX 48 there was no sound card):
https://www.youtube.com/watch?v=GRUQr457zkw (game is from 1987)


I've seen some 1-bit music project on Arduino, but none had gone very far. Until now!

Shiru, the grand-master of 1-bit music on ZX Spectrum, has ported two of his engines on Arduino, from z80 ASM to Arduino in C. And it sounds pretty cool!

Here are the links to these 2 threads on our 1-bit music forum:
http://randomflux.info/1bit/viewtopic.php?pid=1154
http://randomflux.info/1bit/viewtopic.php?pid=1155

you can just download the arduino code, plug an old PC speaker or buzzer on PIN7 and GND of your Arduino, and you'll get great music!


You can of course make your own music with some dedicated trackers, and convert it to play on your arduino!

Here is a link to a quick recording I made with one of the engine:
http://picosong.com/GFmm/

remember, it's not sampled music, it's generated in 1-bit from the arduino itself!


farvardin

#1
Feb 19, 2017, 10:52 am Last Edit: Feb 19, 2017, 01:43 pm by farvardin
Shiru ported 2 new engines!  :)

http://randomflux.info/1bit/viewtopic.php?id=125
http://randomflux.info/1bit/viewtopic.php?id=126

Now we have a total of FOUR 1-bit engines for the Arduino: phaser1, tritone, qchan and octode!


zoomx

Forum requires registration to download engines.

DVDdoug

Quote
you can just download the arduino code, plug an old PC speaker or buzzer on PIN7 and GND of your Arduino, and you'll get great music!
A piezo speaker is OK, but a regular 4 or 8-Ohm speaker will draw excess current.  The "absolute maximum" current from an Arduino I/O pin is 40mA (125 Ohms minimum).

MarkT

You can get pseudo-16 bit quality from 8 bit PWM with noise-shaping too if you want.  I tried that on the Propeller chip but not the Arduino yet - maybe one day.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

MarkT

#5
Apr 05, 2017, 12:33 am Last Edit: Apr 05, 2017, 12:40 am by MarkT
Well I couldn't resist having a go, here is a sketch to demonstrate using timer2 on ATmega328:

Code: [Select]

/* noise shaping filtered 8 bit PWM */

/* For ATmega328, timer2 specific code */

void setup()
{
  analogWrite (11, 50) ;  // enable the output pin and its timer
 
  TCCR2A = 0xB3 ;  // configure as fast 8-bit PWM (mode 011, clock prescale = 1)
  TCCR2B = 0x01 ;
  TIMSK0 = 0x00;   // turn off timer0 interrupts to improve jitter
  TIMSK2 = 0x01;   // timer2 overflow enabled every 256 cycles (62.5 kHz)
  pinMode (13, OUTPUT) ;
}

// circle code for generating sinusoid efficiently in integer maths

long amplitude = 10000L << 16 ;   // fairly low amplitude to make noise/quantization easier to hear
long x = amplitude;
long y = 0 ;

int counter = 0 ;
boolean mode = false ;  // switches between plain 8 bit and noise-shaped regularly so can hear difference

// gives approx 40Hz tone
long next_sine ()
{
  long dx = (-y + 128) >> 8 ;  // fast unbiased signed divide by 256
  long dy = (x + 128) >> 8 ;
  x += dx ;
  y += dy ;
  if (x > amplitude)   // (crudely) prevent gradual increase of amplitude causing runaway.
    x = amplitude ;

  // switch mode every 65536 interrupts
  counter ++ ;
  if (counter == 0)
  {
    mode = !mode ;   // change mode every 1.048s
    digitalWrite (13, mode) ;
  }
  return x  ;
}

// Note that volatile is not needed as only the ISR is touching any variables after initialization.

// noise shaping filter accumulators for the integration stages, 32 bit precision
long acc = 0, acc2 = 0 ;

ISR (TIMER2_OVF_vect)
{
  long sample = next_sine () ;
  char top ;
  if (mode)   // then-case is noise-filtered (pin 13 LED on)
  {
    // two integration steps
    acc  += sample ;
    acc2 += acc ;
    // pick MSBs for PWM
    top = acc2 >> 24   ;
    // negative feedback to the integrator taps
    long feedback = ((long)top) << 24 ;
    acc  -= feedback ;
    acc2 -= feedback ;
  }
  else // else case is plain 8 bit PWM, truncating off LSBs (pin 13 LED off)
  {
    top = sample >> 24 ;
  }
  // output to PWM hardware with 50% duty cycle offset
  OCR2A = top + 0x80 ;
}


// nothing to do in loop
void loop ()
{}


Just hook a speaker/headphones to pin 11 (via current limiting resistor and blocker cap - I used 120
ohms and 470uF into headphones.)

The noise-shaped sinusoid lacks the quantization buzz, replacing it with a quieter high
frequency hiss (the quantization noise is mainly pushed up the spectrum where its less
intrusive and less correlated with the signal).  Its similar to techniques used for dithering image
pixel data in a limited depth palette.  (For instance: http://productionadvice.co.uk/when-to-dither/)

Similarly 1-bit audio is like dithering a grey-scale image from black and white pixels.

My sketch alternates between noise-shaped and straight 8-bit PWM to make the effect clearer.
The signal is generated in 32 bit precision.

More about noise-shaping here https://www.maximintegrated.com/en/app-notes/index.mvp/id/1870

My noise-shaping filter is basically a couple of integrators which are very efficient to code - higher
order filters do better, but a DSP would be needed.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up