Really bad retro style music on the Raspberry Pi using a Pibrella

CAM00595Last week I ordered a Pibrella from Pimoroni & it arrived yesterday but unfortunately the better half was home and i’m not allowed to make any noise. My work area is in the same room as the TV & and when she’s watching her shows we all have to be quiet :) So as soon as she left to go to work this morning I ripped open the anti-static bag the Pibrella came in and started to play. The Pibrella in my opinion is the best bang for the buck addon for the Raspberry Pi. You get a button, a set of traffic light leds, some other leds and a few i/o connectors and i’d been trying to find an excuse to buy one since it came out & I remembered a program that came on the introtape for the 80s British Home Micro, the 48k/96k Camputers Lynx that drew, for the time a pretty graphic & squawked J.S. Bach’s Well Tempered Clavier or a reasonable facsimile thereof from it’s speaker.lynxbach
So I loaded a copy of the Lynx introtape into my copy of the Camputers Lynx emulator PALE (I used the windows version of the emulator but I do have a version that i’ve ported so that it runs on the Raspberry Pi) and LLIST’ed the source.

100 PROTECT 0
110 VDU 2,0,1,7,4,24
120 PRINT @ 40,50;"STOP THE TAPE";CHR$(h25);
130 PAUSE 30000
140 CLS
150 LABEL DRAW
160 READ N
170 IF N=INF THEN GOTO LABEL EYES
180 READ A,B
190 MOVE A,B
200 FOR J=1 TO N
210 READ A,B
220 DRAW A,B
230 NEXT J
240 GOTO LABEL DRAW
250 D
ATA 18
260 REM FACE
270 DATA 140,40,142,80,130,100,110,110,100,100,90,100,75,90,70,70
280 DATA 70,50,75,35,95,30,100,20,115,25,120,28,126,20,128,25,135,30,140,20,140,40
290 REM HAIR
300 DATA 6
310 DATA 40,120,37,117,35,114,40,110,40,106,44,100,44,104
320 DATA 13
330 DATA 44,100,37,97,39,80,45,72,40,69,45,55,50,52
340 DATA 45,38,50,25,60,12,66,9,90,6,95,10,97,4
350 DATA 15
360 DATA 97,4,130,2,135,6,147,14,147,20,153,27,158,40,154,45,160,50,165,53,170,70
370 DATA 171,75,160,78,170,82,171,85,180,124
380 REM NECK
390 DATA 6
400 DATA 75,90,75,110,95,130,133,115,133,99,147,108,178,120
410 REM EYES
420 DATA 7
430 DATA 135,45,131,43,123,43,117,47,128,70,125,74,123,72,118,71
440 DATA 3
450 DATA 118,427,113,45,100,45,95,48
460 DATA 3
470 DATA 110,88,112,86,124,84,132,84
480 DATA 4
490 DATA 111,53,108,50,99,54,108,60,111,53
500 DATA 4
510 DATA 132,52,129,49,120,53,129,59,132,52
520 DATA INF
530 LABEL EYES
540 PRINT @ 62,48;CHR$(21);"o";CHR$(22);"*"; @ 52,50,;"o";CHR$(22);"*";CHR$(20);
550 LABEL TITLE
560 INK GREEN
570 PRINT @ 3,170;"PRELUDE from the WELL-TEMPERED"
580 PRINT "CLAVIER by J.S. Bach"
590 DIM A(40),B(40)
600 FOR J=0 TO 40
610 LET A(J)=INT(2000*(0.5)4**(J/12))
620 LET B(J)=15000/A(J)
630 NEXT J
640 RESTORE 700
650 FOR j=1 TO 68*8
660 READ A
670 BEEP A(A),B(A),63
680 NEXT j
690 BEEP A(10),B(10)0*16,63
700 DATA 10,14,17,22,26,17,22,26
710 DATA 10,14,17,22,26,17,22,26
720 DATA 10,12,19,24,27,19,24,27
730 DATA 10,12,19,24,27,19,24,27
740 DATA 9,12,17,24,27,17,24,27
750 DATA 9,12,17,24,27,17,24,27
760 DATA 10,14,17,22,26,17,22,26
770 DATA 10,14,17,22,26,17,22,26
780 DATA 10,14,19,26,31,19,26,31
790 DATA 10,14,19,26,31,109,26,31
800 DATA 10,12,16,19,24,16,19,24
810 DATA 10,12,16,19,24,16,19,24
820 DATA 9,12,17,24,29,17,24,29
830 DATA 9,12,17,24,29,17,24,29
840 DATA 9,10,14,17,22,14,17,22
850 DATA 9,10,14,17,22,14,17,22
860 DATA 7,10,14,17,22,14,17,22
870 DATA 7,10,14,17,22,14,17,22
880 DATA 12,7,12,16,22,12,16,22
890 DATA 12,7,12,16,22,12,16,22
900 DA,TA 5,9,12,17,21,12,17,21
910 DATA 5,9,12,17,21,12,17,21
920 DATA 5,8,14,17,23,14,17,23
930 DATA 5,8,14,17,23,14,17,23
940 DATA 3,7,12,19,24,12,19,24
950 DATA 3,7,12,19,24,12,19,24
960 DATA 3,6,12,15,21,12,15,21
970 DATA 3,6,12,15,21,12,15,21
980 DATA 2,5,10,17,22,10,17,22
990 DATA 2,5,10,17,22,10,17,22
1000 DATA 14,15,19,22,27,19,22,27
1010, DATA 14,15,19,22,27,19,22,27
1020 DATA 12,15,19,22,27,19,22,27
1030 DATA 12,15,19,22,27,19,22,27
1040 DATA 5,12,17,21,27,17,21,27
1050 DATA 5,12,17,21,27,17,21,27
1060 DATA 10,14,17,22,26,17,22,26
1070 DATA 10,14,17,22,26,17,22,26
1080 DATA 10,17,20,22,26,20,22,26
1090 DATA 10,17,20,22,26,20,22,26
1100 DATA 3,15,19,22,26,19,22,26
1110 DATA 3,,15,19,22,26,19,22,26
1120 DATA 4,10,19,22,25,19,22,25
1130 DATA 4,10,19,22,25,19,22,25
1140 DATA 6,15,21,22,24,21,22,24
1150 DATA 6,15,21,22,24,21,22,24
1160 DATA 5,15,17,21,24,17,21,24
1170 DATA 5,15,17,21,24,17,21,24
1180 DATA 5,14,17,22,26,17,22,26
1190 DATA 5,14,17,22,26,17,22,26
1200 DATA 5,12,17,22,27,17,22,27
1210 DATA 5,12,17,22,27,17,22,27,
1220 DATA 5,12,17,21,27,17,21,27
1230 DATA 5,12,17,21,27,17,21,27
1240 DATA 5,13,19,22,28,19,22,28
1250 DATA 5,13,19,22,28,19,22,28
1260 DATA 5,14,17,22,29,17,22,29
1270 DATA 5,14,17,22,29,17,22,29
1280 DATA 5,12,17,22,27,17,22,27
1290 DATA 5,12,17,22,27,17,22,27
1300 DATA 5,12,17,21,27,17,21,27
1310 DATA 5,12,17,21,27,17,21,27
1320 DAT7A 10,10,17,20,26,17,20,26
1330 DATA 10,10,17,20,26,17,20,26
1340 DATA 10,10,15,19,22,27,22,19
1350 DATA 22,19,15,19,15,12,15,12
1360 DATA 10,9,17,21,24,27,24,21
1370 DATA 24,21,17,21,12,15,14,12
1380 INK BLACK
1390 MOVE 110,88
1400 DRAW 112,86
1410 DRAW 124,84
1420 INK WHITE
1430 DRAW ,112,83
1440 DRAW 110,81
1450 DOT 133,83
1460 PRINT
1470 PRINT
1480 PRINT "For details of the BEEP command"
1490 PRINT "see Chapter 14 of the Lynx user manual."
1500 PAUSE 30000
1510 VDU 24,1,2
1520 PRINT @ 3,70;"END OF PART A";
1530 VDU 25,1,7,23

As I know the Pibrella works with Python, i didn’t bother to check whether it’s supported in wiringPI or RTB, but I assume it does and that’s left for another rainy day (It’s currently persisting down here in NYC at the moment and for the foreseeable future). So after reading through all the DATA statements & running through the code by hand I wrote a VERY BAD python, pygame, RPi.GPIO equivilent of the LynxBASIC program. I know they are not really the notes that I am getting the Pibrella to play due to the fact that the Pibrella doesn’t control the piezo speaker the same way that the Camputers Lynx controlled it’s speaker but they are near enough considering that the Pibrella’s speaker isn’t really supposed to be used for music and i’m really kind of pushing things but it does sound pretty much like how I remember the Lynx sounding on a bad day :) For the values to use I used a chart from Lynx User Issue, Summer 1983.

scale

 

Disclaimer: I don’t know how to program in Python so I grabbed a example from here, a snippet from there and a bit from over yonder.

#! /usr/bin/env python
import pygame
import RPi.GPIO as gpio
import time
pygame.init()
width = 256
height = 248
face=[140,40,142,80,130,100,110,110,100,100,90,100,75,90,70,70,70,50, \
 75,35,95,30,100,20,115,25,120,28,126,20,128,25,135,30,140,20,140,40]
hair=[40,120,37,117,35,114,40,110,40,106,44,100,44,104,44,100,37,97,39,\
 80,45,72,40,69,45,55,50,52,45,38,50,25,60,12,66,9,90,6,95,10,97,\
 4,97,4,130,2,135,6,147,14,147,20,153,27,158,40,154,45,160,50,165,\
 53,170,70,171,75,160,78,170,82,171,85,180,124]
neck=[75,90,75,110,95,130,133,115,133,99,147,108,178,120]
nose=[135,45,131,43,123,43,117,47,128,70,125,74,123,72,118,71]
eyebrow=[118,47,113,45,100,45,95,48]
mouth=[110,88,112,86,124,84,132,84]
eyeleft=[111,53,108,50,99,54,108,60,111,53]
eyeright=[132,52,129,49,120,53,129,59,132,52]
frequency=[110,116.5,123.5,130.8,138.6,146.8,155.6,164.6,174.6,185,196,207.7,\
 220,233.3,246.9,261.1,277.2,293.7,311.1,329.6,349.2,370,392,415.3,\
 440,466.2,493.9,523.2,554.4,587.3,622.3,659.3,698.5,740,784,830.6,\
 880,932.3,987.8,1046.5,1108.7,1174.7,1244.5,1318.5,1396.9,1480,1568,\
 1661.2,1760,1864.6,1975.5,2093,2217.5,2349.3,2489,2637,2793.9,2959.6,\
 3135.9,3322.4]
duration=[909,857.3,809.7,764.5,721.5,681.2,642.7,606.8,572.7,540.5,510.2,\
 481.5,454.5,428.6,405,383,369.7,340.5,321.5,303.4,270.3,255,240.8,\
 227.3,214.5,202.5,191,180.3,170.3,160.7,151.7,143.2,135,127.5,120.4,\
 113.6,107.3,101.3,95.5,90.2,85.3,80.3,75.8,71.6,67.5,63.8,60.2,56.8,\
 53.6,50.6,47.8,45,42.6,40.2,37.9,35.8,33.8,31.9,30]
welltemperedclavier=[10,14,17,22,26,17,22,26,10,14,17,22,26,17,22,26,10,\
 12,19,24,27,19,24,27,10,12,19,24,27,19,24,27,9,12,17,\
 24,27,17,24,27,9,12,17,24,27,17,24,27,10,14,17,22,26,17,\
 22,26,10,14,17,22,26,17,22,26,10,14,19,26,31,19,26,31,\
 10,14,19,26,31,10,9,26,31,10,12,16,19,24,16,19,24,10,12,\
 16,19,24,16,19,24,9,12,17,24,29,17,24,29,9,12,17,24,29,\
 17,24,29,9,10,14,17,22,14,17,22,9,10,14,17,22,14,17,22,\
 7,10,14,17,22,14,17,22,7,10,14,17,22,14,17,22,12,7,12,\
 16,22,12,16,22,12,7,12,16,22,12,16,22,5,9,12,17,21,12,\
 17,21,5,9,12,17,21,12,17,21,5,8,14,17,23,14,17,23,5,8,\
 14,17,23,14,17,23,3,7,12,19,24,12,19,24,3,7,12,19,24,12,\
 19,24,3,6,12,15,21,12,15,21,3,6,12,15,21,12,15,21,2,5,10,\
 17,22,10,17,22,2,5,10,17,22,10,17,22,14,15,19,22,27,\
 19,22,27,14,15,19,22,27,19,22,27,12,15,19,22,27,19,22,\
 27,12,15,19,22,27,19,22,27,5,12,17,21,27,17,21,27,5,12,\
 17,21,27,17,21,27,10,14,17,22,26,17,22,26,10,14,17,22,\
 26,17,22,26,10,17,20,22,26,20,22,26,10,17,20,22,26,20,\
 22,26,3,15,19,22,26,19,22,26,3,15,19,22,26,19,22,26,4,\
 10,19,22,25,19,22,25,4,10,19,22,25,19,22,25,6,15,21,22,\
 24,21,22,24,6,15,21,22,24,21,22,24,5,15,17,21,24,17,21,\
 24,5,15,17,21,24,17,21,24,5,14,17,22,26,17,22,26,5,14,\
 17,22,26,17,22,26,5,12,17,22,27,17,22,27,5,12,17,22,27,\
 17,22,27,5,12,17,21,27,17,21,27,5,12,17,21,27,17,21,27,\
 5,13,19,22,28,19,22,28,5,13,19,22,28,19,22,28,5,14,17,\
 22,29,17,22,29,5,14,17,22,29,17,22,29,5,12,17,22,27,17,\
 22,27,5,12,17,22,27,17,22,27,5,12,17,21,27,17,21,27,5,\
 12,17,21,27,17,21,27,10,10,17,20,26,17,20,26,10,10,17,\
 20,26,17,20,26,10,10,15,19,22,27,22,19,22,19,15,19,15,\
 12,15,12,10,9,17,21,24,27,24,21,24,21,17,21,12,15,14,12]
screen = pygame.display.set_mode((width, height))
white = pygame.Color("white")
for i in range(0,len(face)-2,2):
 pygame.draw.line(screen,white,(face[i],face[i+1]),(face[i+2],face[i+3]))
 pygame.display.flip()
 time.sleep(0.05)
for i in range(0,len(hair)-2,2):
 pygame.draw.line(screen,white,(hair[i],hair[i+1]),(hair[i+2],hair[i+3]))
 pygame.display.flip()
 time.sleep(0.05)
for i in range(0,len(neck)-2,2):
 pygame.draw.line(screen,white,(neck[i],neck[i+1]),(neck[i+2],neck[i+3]))
 pygame.display.flip()
 time.sleep(0.05)
for i in range(0,len(nose)-2,2):
 pygame.draw.line(screen,white,(nose[i],nose[i+1]),(nose[i+2],nose[i+3]))
 pygame.display.flip()
 time.sleep(0.05)
for i in range(0,len(eyebrow)-2,2):
 pygame.draw.line(screen,white,(eyebrow[i],eyebrow[i+1]),(eyebrow[i+2],eyebrow[i+3]))
 pygame.display.flip()
 time.sleep(0.05)
for i in range(0,len(mouth)-2,2):
 pygame.draw.line(screen,white,(mouth[i],mouth[i+1]),(mouth[i+2],mouth[i+3]))
 pygame.display.flip()
 time.sleep(0.05)
for i in range(0,len(eyeleft)-2,2):
 pygame.draw.line(screen,white,(eyeleft[i],eyeleft[i+1]),(eyeleft[i+2],eyeleft[i+3]))
 pygame.display.flip()
 time.sleep(0.05)
for i in range(0,len(eyeright)-2,2):
 pygame.draw.line(screen,white,(eyeright[i],eyeright[i+1]),(eyeright[i+2],eyeright[i+3]))
 pygame.display.flip()
 time.sleep(0.05)
myfont = pygame.font.SysFont("monospace",14)
pupil = myfont.render("*",1,(255,0,0))
screen.blit(pupil,(103,48))
screen.blit(pupil,(125,48))
tl1 = myfont.render("PRELUDE from the WELL-TEMPERED", 1, (0,255,0))
tl2 = myfont.render("CLAVIER by J.S. Bach", 1, (0,255,0))
tl3 = myfont.render("Ported from LynxBasic to Python", 1, (0,255,0))
tl4 = myfont.render("For Pibrella by ukscone", 1, (0,255,0))
screen.blit(tl1,(3,170))
screen.blit(tl2,(3,185))
screen.blit(tl3,(3,199))
screen.blit(tl4,(3,215))
pygame.display.flip()
PIEZOSPEAKER = 18
gpio.setmode(gpio.BCM)
gpio.setwarnings(True)
gpio.setup(PIEZOSPEAKER,gpio.OUT)
p = gpio.PWM(PIEZOSPEAKER,300)
p.start(1)
for x in xrange(0,len(welltemperedclavier)):
 p.ChangeFrequency(frequency[welltemperedclavier[x]])
 time.sleep(duration[welltemperedclavier[x]]*0.001)
p.stop()
gpio.cleanup()
pygame.quit()

See I told you I am rubbish at python 😀 But it works mostly as expected.

If you listen really carefully and let your imagination fly it just about sounds like Prelude to Well Tempered Clavier. You can just about hear a few notes :)

YouTube Preview Image

 

 

 

Share

2 Comments

Add a Comment
  1. I still don’t have the note length right & will have to have a think about it but it’s good enough for the time being until I get some time to play more. in the meantime i’ve put the source into my file dump and also added a version (note duration still not right either) that uses the forumula from the LynxBASIC program to fill the frequency/duration array (list?) rather than having to type them in. The files are at http://russelldavis.org/Files/files/bach or by using the files link in the menu

Leave a Reply

Your email address will not be published. Required fields are marked *

 

russelldavis.org © 2014 Frontier Theme