Last active
February 20, 2018 03:01
-
-
Save rbnpi/4bb22958b8c34ed0eeb8afcd3d5c73ee to your computer and use it in GitHub Desktop.
Sonic Pi 3 Musical Talking Clock tested on Pi3 and Mac. Requires download of samples http://r.newman.ch/rpi/clock/count2.zip Video of operation at https://youtu.be/N9v3V32cFU8
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Analog clock with figures based on example on Processing Site, and various on YouTube | |
//adjusted by Robin Newman | |
//add SansSerif-18.vlw from Tools->Create Font... (smooth) | |
int cx, cy; | |
float secondsRadius; | |
float minutesRadius; | |
float hoursRadius; | |
float clockDiameter; | |
void setup() { | |
size(400,400); | |
stroke(255); | |
PFont font = loadFont("SansSerif-18.vlw"); | |
textFont(font); | |
textAlign(CENTER,CENTER); | |
float radius = min(width/1.2, height/1.2) / 2; | |
secondsRadius = radius * 0.72; | |
minutesRadius = radius * 0.60; | |
hoursRadius = radius * 0.50; | |
clockDiameter = radius * 1.8; | |
cx = width / 2; | |
cy = height / 2; | |
} | |
void draw() { | |
background(20,20,255); //adjust as desired | |
// Draw the clock background | |
fill(0); | |
noStroke(); | |
ellipse(cx, cy, clockDiameter, clockDiameter); | |
// Angles for sin() and cos() start at 3 o'clock; | |
// subtract HALF_PI to make them start at the top | |
float s = map(second(), 0, 60, 0, TWO_PI) - HALF_PI; | |
float m = map(minute() + norm(second(), 0, 60), 0, 60, 0, TWO_PI) - HALF_PI; | |
float h = map(hour() + norm(minute(), 0, 60), 0, 24, 0, TWO_PI * 2) - HALF_PI; | |
// Draw the hands of the clock | |
stroke(255); | |
strokeWeight(1); | |
line(cx, cy, cx + cos(s) * secondsRadius, cy + sin(s) * secondsRadius); | |
strokeWeight(2); | |
line(cx, cy, cx + cos(m) * minutesRadius, cy + sin(m) * minutesRadius); | |
strokeWeight(4); | |
line(cx, cy, cx + cos(h) * hoursRadius, cy + sin(h) * hoursRadius); | |
// Draw the dots arround the clock | |
strokeWeight(2); | |
beginShape(POINTS); | |
for (int a = 0; a < 360; a+=6) { | |
float angle = radians(a); | |
float x = cx + cos(angle) * secondsRadius; | |
float y = cy + sin(angle) * secondsRadius; | |
if( a%30 !=0){ //miss out point if numerical position for hour | |
vertex(x, y);} | |
} | |
endShape(); | |
int hour = 3; | |
for (int a = 0; a < 360; a+=30) { | |
float angle = radians(a); | |
float x = cx + cos(angle) * secondsRadius; | |
float y = cy + sin(angle) * secondsRadius; | |
vertex(x, y); //add point to show minite position on face | |
fill(255); | |
//text(hour, x, y); | |
textSize(18); | |
text(Integer.toString(hour),x,y); | |
hour++; | |
if(hour > 12){ | |
hour = 1; | |
} | |
} | |
textSize(22); | |
text("Sonic Pi Musical Talking Clock", width/2,20); | |
text("Clock Face by Processing Script", width/2,height-30); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#Sonic Pi playing and speaking clock by Robin Newman version 0.8 | |
#requires samples saying zero,1-20,30,40 and 50 saved in that order in a folder | |
#uses Ruby Time.now function to get time info. This is split up by functions | |
#hours, mins and secs to give three integer values (24 hour clock) | |
use_debug false | |
use_sched_ahead_time 0 #important no delays for a clock! | |
#adjust next four lines to select options | |
set :enabletenths,true | |
set :enablesecs,true | |
set :enablespeech,true | |
set :enablechimes,true | |
path ="~/Desktop/count2" #path to samples | |
load_samples path | |
load_sample :ambi_glass_rub | |
use_synth :saw #synth to play notes | |
#first section defines data for quarter,half,three quarters and hour chimes | |
quarter= [:a3,:g3,:f3,:c3] | |
k=0.629 #intetchime gap (from Westminster Clock) | |
qd=[k]*4 #durations for quarter chimes | |
rv=[0.822] #gap between groups of four chimes | |
half=[:f3,:a3,:g3,:c3, :r, :c3,:g3,:a3,:f3] | |
hd=[k]*4+ rv +[k]*4 #durations for half chimes | |
hrv=5 #gap between hour chimes and first "bong" for the hour | |
threequarters=[:a3,:f3,:g3,:c3, :r, :c3,:g3,:a3,:f3, :r, :a3,:g3,:f3,:c3] | |
tqd=[k]*4+rv+[k]*4+rv+[k]*4 #durations for threequarter chimes | |
hour=[:f3,:a3,:g3,:c3, :r, :f3,:g3,:a3,:f3, :r, :a3,:f3,:g3,:c3, :r, :c3,:g3,:a3,:f3] | |
hd=[k]*4+rv+[k]*4+rv+[k]*4+rv+[k]*4 #durations for hour chimes | |
anticipate=16*k+3*rv[0]+hrv #start hour chimes this time BEFORE the hour | |
anticipateInt=anticipate.to_int #int part of anticipate | |
anticipateFrac=anticipate-anticipateInt #fractional part of anticipate | |
#puts anticipateInt, anticipateFrac | |
bong=:c3 #pitch for "bongs" also an octave higher | |
define :playsamp do |n,k| #function to play sample for chimes and "bong" | |
mutechimes=0 | |
mutechimes=1 if get(:enablechimes) | |
sample :ambi_glass_rub,start: 0.09,sustain: 0.1*k,release: 0.9*k,amp: 0.6*mutechimes,rpitch: note(n)-note(:fs4) | |
end | |
define :playchimes do |notes,durations| #function to play series of notes with sample | |
notes.zip(durations).each do |n,d| | |
playsamp n,d if n !=:r | |
sleep d | |
end | |
end | |
define :playbongs do |n| | |
#function to play bongs with gverb | |
n=12 if n==0 #play 12 "bongs" at midnight not zero | |
n=n-12 if n>12 | |
n.times do | |
playsamp bong, k | |
playsamp note(bong+12),k | |
sleep 1 | |
end | |
end | |
#end of chimes setup | |
define :speak do |n,hm=0| #n is integer 0...59, v allows volums change if required | |
mutesample=0 | |
mutesample=1 if get(:enablespeech) #don't mute whole live loop so still get printout | |
f=n/10 #first digit | |
s=n%10 #second digit | |
if n < 21 #up to 20 recorded as a single sample | |
puts "Number to speak is #{n}" | |
sample path,n ,amp: mutesample | |
sleep 0.75 #gap before next sample | |
else #above 20 may require two samples to speak | |
puts "Two samples to speak: #{f*10}, #{s}" if s>0 | |
puts "Number to speak is #{f*10}" if s==0 | |
sample path,[20,21,22,23][f-2],amp: mutesample #f-2 gives offset to required sample | |
sleep 0.75 #gap between speaking samples | |
sample path,s ,amp: mutesample if s>0 | |
sleep 0.75 if s > 0 #extra gap if two samples used | |
end | |
if get(:enablespeech) #only add further speech if enabled | |
sample path,24 if hm==0 #add "seconds" | |
sample path,25 if hm==1 and mins != 1#add "minutes" | |
sample path,25,finish: 0.58 if hm==1 and mins ==1 #cut the s at the end of minute | |
sample path,26 if hm==2 #add "hours" | |
end | |
sleep 0.75 #allows for different sections in the loop to have completed before next pass | |
end | |
define :hours do #returns hours as an integer 0-23 | |
return Time.now.hour | |
end | |
define :mins do #returns minutes as an integer 0-59 | |
return Time.now.min | |
end | |
define :secs do #returns seconds as an integer 0-59 (I have not catered for "60" leap seconds) | |
return (Time.now.sec)%60 | |
end | |
###### The following live loops produce the output in speech,notes and chimes | |
set :inhibit,false #inhibts on the hour when hour "bongs" are played | |
live_loop :announce,sync: :go do #speaks the time every minute in full (apart from when hour is "bonged") | |
if secs == 0 and !get(:inhibit) | |
speak hours,2 | |
#sleep 0.75 | |
speak mins,1 | |
end | |
sleep 0.6 | |
end | |
live_loop :playtime,sync: :go do #play notes for the minutes and announce seconds every 15 seconds | |
cue :chimetest | |
n=scale :c3,:major,num_octaves: 5 #notes to play | |
muteflag=0;muteflag1=0 | |
muteflag=1 if get(:enablesecs) | |
muteflag1=1 if get(:enabletenths) | |
if secs < 30 | |
offset = secs | |
else | |
offset = 60-secs | |
end | |
puts "#{secs} seconds" | |
play n[offset],amp: 0.03*muteflag,release: 1 | |
in_thread do #play tenths of seconds | |
use_synth :beep | |
9.times do |z| | |
play n[offset]+z,amp: 0.05*muteflag1,release: 0.08 | |
sleep 0.1 | |
end | |
play n[offset]+9,amp: 0.05*muteflag1,release: 0.08 #no sleep last time makes thread a bit shorter | |
end | |
in_thread do #announce quarter minutes | |
speak 15 if secs == 15 | |
speak 30 if secs == 30 | |
speak 45 if secs == 45 | |
end | |
sleep 1 #loop takes 1 second to complete. | |
end | |
#section to play chimes and hour "bongs" Use separate live_loop for each to try and minimise timeouts. cue from playtime loop | |
with_fx :gverb,room: 15,mix: 0.8 do | |
live_loop :chimesQ,sync: :go do | |
sync :chimetest | |
if mins==25 and secs==0 #check for 15 mins past hour | |
playchimes quarter,qd #play quarter chimes | |
sleep 12 #allow time for chimes to finish | |
end | |
end | |
live_loop :chimesH,sync: :go do | |
sync :chimetest | |
if mins==30 and secs==0 #check for 30 mins past hour | |
playchimes half,hd #play half hour chimes | |
sleep 12 #allow time for chimes to finish | |
end | |
end | |
live_loop :chimesT,sync: :go do | |
sync :chimetest | |
if mins==45 and secs==0 #check for 45 mins past hour | |
playchimes threequarters,tqd #play three quarters chimes | |
sleep 12 #allow time for chimes to finish | |
end | |
end | |
live_loop :chimesF,sync: :go do | |
sync :chimetest | |
if mins==59 and secs==60-anticipateInt #anticipate the hour to allow chimes to fimihsh | |
set :inhibit,true | |
sleep anticipateFrac | |
playchimes hour,hd #play chimes for thr hour (in advnace of thr actual hour) | |
sleep 12 #allow time for chimes to finish | |
end | |
end | |
live_loop :chimesB,sync: :go do | |
sync :chimetest | |
if mins== 28 and secs==0 #play a "bong" for each hour at second intervals | |
playbongs hours | |
set :inhibit,false | |
sleep 12 #allow time for chimes to finish | |
end | |
end | |
end | |
######## following code sycs and starts the live loops | |
sleep 2 #wait 2 secs to allow loops and sample loads to settle | |
set :t,secs #store current secs in time state | |
until secs>get(:t) | |
sleep 0.05 | |
end | |
cue :go #trigger loops on a secs change |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Clock gives rising and falling scale for seconds, with faster notes for 1/10th seconds. Speaks at 15,30 and 45 seconds and announces time at the minute. Also plays Westminster Chimes on quarter, half, three-quarter and hour, with "bongs" for the hour.
Each section can be enabled/disabled at the start of the program, which can be re-run whilst running. May occasionally require re-running as timing issues when garbage-collection occurs, as the program is run with zero sched_ahead_time. You need to download and install the speech samples from http://r.newman.ch/rpi/clock/count2.zip and place them in a suitable location pointed to by line 13 of the Sonic Pi program. Also included is an optional Processing .pde file which can be run with Processing 3 to give further visual output. It is not connected to the Sonic Pi program at all. Note you should add a font SansSerif-18.vlw from the Tools...Create menu in Processing to run this,