Metronome: Difference between revisions
m (→{{header|Liberty BASIC}}: whitespace) |
(→Tcl: Added implementation) |
||
Line 182: | Line 182: | ||
wait</lang> |
wait</lang> |
||
=={{header|Tcl}}== |
|||
This code only rings the bell on the high beat, which occurs at the start of the bar. |
|||
<lang tcl>package require Tcl 8.5 |
|||
lassign $argv bpm bpb |
|||
if {$argc < 2} {set bpb 4} |
|||
if {$argc < 1} {set bpm 60} |
|||
fconfigure stdout -buffering none |
|||
set intervalMS [expr {round(60000.0 / $bpm)}] |
|||
set ctr 0 |
|||
proc beat {} { |
|||
global intervalMS ctr bpb |
|||
after $intervalMS beat ;# Reschedule first, to encourage minimal drift |
|||
if {[incr ctr] == 1} { |
|||
puts -nonewline "\r\a[string repeat { } [expr {$bpb+4}]]\rTICK" |
|||
} else { |
|||
puts -nonewline "\rtick[string repeat . [expr {$ctr-1}]]" |
|||
} |
|||
if {$ctr >= $bpb} { |
|||
set ctr 0 |
|||
} |
|||
} |
|||
# Run the metronome until the user uses Ctrl+C... |
|||
beat |
|||
vwait forever</lang> |
|||
It might be executed like this: |
|||
<lang bash>tclsh8.5 metronome.tcl 90 4</lang> |
|||
{{omit from|AWK|Does not have timing facilities}} |
{{omit from|AWK|Does not have timing facilities}} |
Revision as of 14:34, 26 September 2011
The task is to implement a metronome. The metronome should be capable of producing high and low audio beats, accompanied by a visual beat indicator, and the beat pattern and tempo should be configurable.
For the purpose of this task, it is acceptable to play sound files for production of the beat notes, and an external player may be used. However, the playing of the sounds should not interfere with the timing of the metronome.
The visual indicator can simply be a blinking red or green area of the screen (depending on whether a high or low beat is being produced), and the metronome can be implemented using a terminal display, or optionally, a graphical display, depending on the language capabilities. If the language has no facility to output sound, then it is permissible for this to implemented using just the visual indicator.
C
Using usleep
with self correcting delays. Audio is the bell character, which will definitely drive one insane (but I'm ok: my computer doesn't have the bell device). Invoke with ./a.out [beats_per_minute]
, default to 60.
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <unistd.h>
- include <stdint.h>
- include <signal.h>
- include <time.h>
- include <sys/time.h>
struct timeval start, last;
inline int64_t tv_to_u(struct timeval s) { return s.tv_sec * 1000000 + s.tv_usec; }
inline struct timeval u_to_tv(int64_t x) { struct timeval s; s.tv_sec = x / 1000000; s.tv_usec = x % 1000000; return s; }
void draw(int dir, int64_t period, int64_t cur, int64_t next) { int len = 40 * (next - cur) / period; int s, i;
if (len > 20) len = 40 - len; s = 20 + (dir ? len : -len);
printf("\033[H"); for (i = 0; i <= 40; i++) putchar(i == 20 ? '|': i == s ? '#' : '-'); }
void beat(int delay) { struct timeval tv = start; int dir = 0; int64_t d = 0, corr = 0, slp, cur, next = tv_to_u(start) + delay; int64_t draw_interval = 20000; printf("\033[H\033[J"); while (1) { gettimeofday(&tv, 0); slp = next - tv_to_u(tv) - corr; usleep(slp); gettimeofday(&tv, 0);
putchar(7); /* bell */ fflush(stdout);
printf("\033[5;1Hdrift: %d compensate: %d (usec) ", (int)d, (int)corr); dir = !dir;
cur = tv_to_u(tv); d = cur - next; corr = (corr + d) / 2; next += delay;
while (cur + d + draw_interval < next) { usleep(draw_interval); gettimeofday(&tv, 0); cur = tv_to_u(tv); draw(dir, delay, cur, next); fflush(stdout); } } }
int main(int c, char**v) { int bpm;
if (c < 2 || (bpm = atoi(v[1])) <= 0) bpm = 60; if (bpm > 600) { fprintf(stderr, "frequency %d too high\n", bpm); exit(1); }
gettimeofday(&start, 0); last = start; beat(60 * 1000000 / bpm);
return 0; }</lang>
Liberty BASIC
Requires two supplied wav files for accentuated & standard sounds. <lang lb> WindowWidth =230
WindowHeight =220
button #w.b1 "Start", [start], LR, 110, 90, 55, 20 button #w.b2 "Tempo", [tempo], LR, 180, 90, 55, 20 button #w.b3 "Pattern", [pattern], LR, 40, 90, 55, 20
open "Metronome" for graphics_nsb_nf as #w
#w "trapclose quit" #w "down" #w "fill darkblue ; backcolor darkblue ; color white"
tempo = 60 ' per minute interval =1000 /(tempo /60) ' timer works in ms tickCount = 0 ' cycle counter running = 1 ' flag for state bar$ = "HLLL" ' initially strong-weak-weak-weak count = len( bar$)
wait
sub quit w$
close #w$ end
end sub
[start]
if running =1 then running =0 #w.b1 "Stop" #w.b2 "!disable" #w.b3 "!disable" else running =1 #w.b1 "Start" #w.b2 "!enable" #w.b3 "!enable" end if if running =0 then timer interval, [tick] else timer 0 wait
[tempo]
prompt "New tempo 30...360"; tempo$ tempo =val( tempo$) tempo =min( tempo, 360) tempo =max( tempo, 30) interval =int( 1000 /(tempo /60)) wait
[pattern]
prompt "New Pattern, eg 'HLLL' "; bar$ count =len( bar$) if count <2 or count >8 then goto [pattern]
wait
[tick]
'beep and flash #w "place 115 40"
if mid$( bar$, tickCount +1, 1) ="H" then playwave "mHi.wav", async #w "backcolor blue ; color white ; circlefilled "; 20 -tickCount *2 else playwave "mLo.wav", async #w "backcolor cyan ; circlefilled "; 20 -tickCount *2 end if
#w "place 50 140 ; backcolor darkblue ; color white" #w "\ "; tempo; " beats /min." #w "place 85 160" #w "\"; bar$
#w "place 85 120" #w "\Beat # "; tickCount +1
#w "place 115 40" #w "color darkblue"
tickCount =( tickCount +1) mod count
#w "flush"
wait</lang>
Tcl
This code only rings the bell on the high beat, which occurs at the start of the bar. <lang tcl>package require Tcl 8.5
lassign $argv bpm bpb if {$argc < 2} {set bpb 4} if {$argc < 1} {set bpm 60}
fconfigure stdout -buffering none set intervalMS [expr {round(60000.0 / $bpm)}] set ctr 0
proc beat {} {
global intervalMS ctr bpb after $intervalMS beat ;# Reschedule first, to encourage minimal drift if {[incr ctr] == 1} {
puts -nonewline "\r\a[string repeat { } [expr {$bpb+4}]]\rTICK"
} else {
puts -nonewline "\rtick[string repeat . [expr {$ctr-1}]]"
} if {$ctr >= $bpb} {
set ctr 0
}
}
- Run the metronome until the user uses Ctrl+C...
beat vwait forever</lang> It might be executed like this: <lang bash>tclsh8.5 metronome.tcl 90 4</lang>