Movatterモバイル変換


[0]ホーム

URL:


Jump to content
Rosetta Code
Search

Metronome

From Rosetta Code
Task
Metronome
You are encouraged tosolve this task according to the task description, using any language you may know.

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.

11l

Translation of:Python
F main(bpm = 72, bpb = 4)   V t = 60.0 / bpm   V counter = 0   L      counter++      I counter % bpb != 0         print(‘tick’)      E         print(‘TICK’)      sleep(t)main()

Ada

Template:This metronome only does 60 bpm with 1 Measure length.

withAda.Text_IO;useAda.Text_IO;--This package is for the delay.withAda.Calendar;useAda.Calendar;--This package adds soundwithAda.Characters.Latin_1;procedureMainisbeginPut_Line("Hello, this is 60 BPM");loopAda.Text_IO.Put(Ada.Characters.Latin_1.BEL);delay0.9;--Delay in seconds.  If you change to 0.0 the program will crash.endloop;endMain;

AppleScript

setbpmtothetext returnedof(display dialog"How many beats per minute?"defaultanswer60)setpauseBetweenBeepsto(60/bpm)repeatbeepdelaypauseBetweenBeepsendrepeat

Arturo

startMetronome:function[bpm,msr][freq:60000/bpmi:0while[true][loopmsr-1'x[prints"\a"pausefreq]inc'iprint~"\aAND|i|"pausefreq]]tempo:to:integerarg\0beats:to:integerarg\1startMetronometempobeats

AutoHotkey

Rather basic implementation, but meets the requirements and is reasonably accurate.

bpm=120 ; Beats per minutepattern=4/4 ;duration=100 ; Millisecondsbeats=0   ; internal counterGui-CaptionStringSplit,p,pattern,/Start:=A_TickCountLoop{GuiColor,0xFF0000GuiShow,w200h200NaSoundBeep750,durationbeats++Sleep1000*60/bpm-durationLoop%p1-1{GuiColor,0x00FF00GuiShow,w200h200NaSoundBeep,,durationbeats++Sleep1000*60/bpm-duration}}Esc::MsgBox%"Metronome beeped "beats" beats, over "(A_TickCount-Start)/1000" seconds. "ExitApp

AWK

# syntax: GAWK -f METRONOME.AWK@load"time"BEGIN{metronome(120,6,10)metronome(72,4)exit(0)}functionmetronome(beats_per_min,beats_per_bar,limit,beats,delay,errors){print("")if(beats_per_min+0<=0){print("error: beats per minute is invalid");errors++}if(beats_per_bar+0<=0){print("error: beats per bar is invalid");errors++}if(limit+0<=0){limit=999999}if(errors>0){return}delay=60/beats_per_minprintf("delay=%f",delay)while(beats<limit){printf((beats++%beats_per_bar==0)?"\nTICK":" tick")sleep(delay)}}
Output:
delay=0.500000TICK tick tick tick tick tickTICK tick tick tickdelay=0.833333TICK tick tick tickTICK tick tick tick...

BBC BASIC

Works with:BBC BASIC for Windows
BeatPattern$="HLLL"Tempo%=100*fontArial,36REPEATFORbeat%=1TOLEN(BeatPattern$)IFMID$(BeatPattern$,beat%,1)="H"THENSOUND1,-15,148,1ELSESOUND1,-15,100,1ENDIFVDU30COLOUR2PRINTLEFT$(BeatPattern$,beat%-1);COLOUR9PRINTMID$(BeatPattern$,beat%,1);COLOUR2PRINTMID$(BeatPattern$,beat%+1);WAIT6000/Tempo%NEXTUNTILFALSE

C

Usingusleep 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.

#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<stdint.h>#include<signal.h>#include<time.h>#include<sys/time.h>structtimevalstart,last;inlineint64_ttv_to_u(structtimevals){returns.tv_sec*1000000+s.tv_usec;}inlinestructtimevalu_to_tv(int64_tx){structtimevals;s.tv_sec=x/1000000;s.tv_usec=x%1000000;returns;}voiddraw(intdir,int64_tperiod,int64_tcur,int64_tnext){intlen=40*(next-cur)/period;ints,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?'#':'-');}voidbeat(intdelay){structtimevaltv=start;intdir=0;int64_td=0,corr=0,slp,cur,next=tv_to_u(start)+delay;int64_tdraw_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);}}}intmain(intc,char**v){intbpm;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);return0;}


C#

Translation of:Java
usingSystem;usingSystem.Threading;publicclassProgram{publicstaticvoidMain(string[]args){Metronomemetronome1=newMetronome(120,4);metronome1.Start();}}publicclassMetronome{privatedoublebpm;privateintmeasure;privateintcounter;publicMetronome(doublebpm,intmeasure){this.bpm=bpm;this.measure=measure;}publicvoidStart(){Threadthread=newThread(()=>{while(true){try{Thread.Sleep((int)(1000*(60.0/bpm)));}catch(ThreadInterruptedExceptione){Console.WriteLine(e.StackTrace);}counter++;if(counter%measure==0){Console.WriteLine("TICK");}else{Console.WriteLine("TOCK");}}});thread.Start();}}


C++

This example has a timing loop to ensure that its timing is reliable in the long term.

#include<chrono>#include<cstdint>#include<iostream>#include<thread>classMetronome{public:Metronome(constint32_t&aBeats_per_minute,constint32_t&aMeasure,constint32_t&aDuration_in_minutes):beats_per_minute(aBeats_per_minute),measure(aMeasure),duration_in_minutes(aDuration_in_minutes){counter=0;}voidstart(){while(counter<duration_in_minutes*beats_per_minute){start_time=std::chrono::system_clock::now();std::this_thread::sleep_until(time_to_awake());counter++;if(counter%measure!=0){std::cout<<"Tick "<<std::flush;}else{std::cout<<"Tock"<<std::endl;}}}private:std::chrono::system_clock::time_pointtime_to_awake()const{returnstart_time+std::chrono::seconds(1);}std::chrono::system_clock::time_pointstart_time;int32_tcounter;constint32_tbeats_per_minute,measure,duration_in_minutes;};intmain(){Metronomemetronome(60,4,1);metronome.start();}
Output:
Tick Tick Tick TockTick Tick Tick TockTick Tick // continues for the requested duration

Common Lisp

Depends on quicklisp and OpenAL.

(ql:quickload'(cl-openalcl-alc))(defparameter*short-max*(-(expt215)1))(defparameter*2-pi*(*2pi))(defunmake-sin(period)"Create a generator for a sine wave of the given PERIOD."(lambda(x)(sin(**2-pi*(/xperiod)))))(defunmake-tone(lengthfrequencysampling-frequency)"Create a vector containing sound information of the given LENGTH,FREQUENCY, and SAMPLING-FREQUENCY."(let((data(make-array(truncate(*lengthsampling-frequency)):element-type'(signed-byte16)))(generator(make-sin(/sampling-frequencyfrequency))))(dotimes(i(lengthdata))(setf(arefdatai)(truncate(**short-max*(funcallgeneratori)))))data))(defuninternal-time-ms()"Get the process's real time in ms."(*1000(/(get-internal-real-time)internal-time-units-per-second)))(defunspin-wait(next-real-time)"Wait until the process's real time has reached the given time."(loopwhile(<(internal-time-ms)next-real-time)))(defunupload(bufferdatasampling-frequency)"Upload the given vector DATA to a BUFFER at the given SAMPLING-FREQUENCY."(cffi:with-pointer-to-vector-data(data-ptrdata)(al:buffer-databuffer:mono16data-ptr(*2(lengthdata))sampling-frequency)))(defunmetronome(beats/minutepattern&optional(sampling-frequency44100))"Play a metronome until interrupted."(let((ms/beat(/60000beats/minute)))(alc:with-device(device)(alc:with-context(contextdevice)(alc:make-context-currentcontext)(al:with-buffer(low-buffer)(al:with-buffer(high-buffer)(al:with-source(source)(al:sourcesource:gain0.5)(flet((play-it(buffer)(al:sourcesource:bufferbuffer)(al:source-playsource))(upload-it(buffertimefrequency)(uploadbuffer(make-tonetimefrequencysampling-frequency)sampling-frequency)))(upload-itlow-buffer0.1440)(upload-ithigh-buffer0.15880)(let((next-scheduled-tone(internal-time-ms)))(loop(loopforsymbolinpatterndo(spin-waitnext-scheduled-tone)(incfnext-scheduled-tonems/beat)(casesymbol(l(play-itlow-buffer))(h(play-ithigh-buffer)))(princsymbol))(terpri)))))))))))
CL-USER> (metronome 100 '(h l l l))HLLLHLL; Evaluation aborted on NIL.

Delphi

Library: Winapi.Windows
Library: System.SysUtils
Translation of:Java
programMetronome;{$APPTYPE CONSOLE}usesWinapi.Windows,System.SysUtils;procedureStartMetronome(bpm:double;measure:Integer);varcounter:Integer;begincounter:=0;whileTruedobeginSleep(Trunc(1000*(60.0/bpm)));inc(counter);ifcountermodmeasure=0thenwriteln('TICK')elsewriteln('TOCK');end;end;beginStartMetronome(120,4);end.
Output:
TOCKTOCKTOCKTICKTOCKTOCKTOCKTICKTOCKTOCKTOCKTICK^C

EchoLisp

;; available preloaded sounds are : ok, ko, tick, tack, woosh, beep, digit .(lib'timer)(define(metronome)(blink)(play-sound'tack))(at-every1000'metronome);; every 1000 msec;; CTRL-C to stop

F#

openSystemopenSystem.Threading// You can use .wav files for your clicks.// If used, make sure they are in the same file// as this program's executable file.lethigh_pitch=newSystem.Media.SoundPlayer("Ping Hi.wav")letlow_pitch=newSystem.Media.SoundPlayer("Ping Low.wav")letfactorxy=x/y// Notice that exact bpm would not work by using// Thread.Sleep() as there are additional function calls// that would consume a miniscule amount of time.// This number may need to be adjusted based on the cpu.letcpu_error=-750.0letprint=function|1->high_pitch.Play();printf"\nTICK "|_->low_pitch.Play();printf"tick "letwait(time:int)=Thread.Sleep(time)// Composition of functionslettick=float>>factor(60000.0+cpu_error)>>int>>waitletrecplaybeats_per_measurecurrent_beatbeats_per_minute=matchcurrent_beat,beats_per_measurewith|a,b->current_beat|>printbeats_per_minute|>tickifa<>bthenbeats_per_minute|>playbeats_per_measure(current_beat+1)[<EntryPointAttribute>]letmain(args:string[])=lettempo,beats=intargs.[0],intargs.[1]Seq.initInfinite(funi->i+1)|>Seq.iter(fun_->tempo|>playbeats1|>ignore)0

Sample run:

$ metronome 120 6TICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick^C

Factor

USING:accessorscalendarcircularcolors.constantscolors.hsvcommand-linecontinuationsiokernelmathmath.parsernamespacesopenal.examplesequencessystemtimersuiui.gadgetsui.pens.solid;IN:rosetta-code.metronome:bpm>duration(bpm--duration)60swap/seconds;:blink-gadget(gadgetfreq--)1.0 1.0 1.0<hsva><solid>>>interiorrelayout-1;:blank-gadget(gadget--)COLOR:white<solid>>>interiorrelayout-1;:play-note(gadgetfreq--)[blink-gadget][0.3play-sineblank-gadget]2bi;:metronome-iteration(gadgetcircular--)[firstplay-note][rotate-circular]bi;TUPLE:metronome-gadget<gadgetbpmnotestimer;:<metronome-gadget>(bpmnotes--gadget)\ metronome-gadgetnewswap>>notesswap>>bpm;:metronome-quot(gadget--quot)dupnotes>><circular>[metronome-iteration]2curry;:metronome-timer(gadget--timer)[metronome-quot][bpm>>bpm>duration]bievery;M:metronome-gadgetgraft*(gadget--)[metronome-timer]keeptimer<<;M:metronome-gadgetungraft*timer>>stop-timer;M:metronome-gadgetpref-dim*drop{200 200};:metronome-defaults(--bpmnotes)60{440 220 330};:metronome-ui(bpmnotes--)<metronome-gadget>"Metronome"open-window;:metronome-example(--)metronome-defaultsmetronome-ui;:validate-args(int-args--)[length2<][[0<=]any?]bior["args error"throw]when;:(metronome-cmdline)(args--bpmnotes)[string>number]mapdupvalidate-argsunclipswap;:metronome-cmdline(--bpmnotes)command-lineget[metronome-defaults][(metronome-cmdline)]if-empty;:print-defaults(--)metronome-defaultsswapprefix[" "write][number>stringwrite]interleavenl;:metronome-usage(--)"Usage: metronome [BPM FREQUENCIES...]"print"Arguments must be non-zero"print"Example: metronome "writeprint-defaultsflush;:metronome-main(--)[[metronome-cmdlinemetronome-ui][dropmetronome-usage1exit]recover]with-ui;MAIN:metronome-main


FreeBASIC

REM FreeBASIC no tiene la capacidad de emitir sonido de forma nativa.REM La función Sound no es mía, incluyo los créditos correspondientes.' Sound Function v0.3 For DOS/Linux/Win by yetifoot' Credits:'    http://www.frontiernet.net/~fys/snd.htm'    http://delphi.about.com/cs/adptips2003/a/bltip0303_3.htm#ifdef__FB_WIN32__#includeOnce"windows.bi"#endifSubSound_DOS_LIN(ByvalfreqAsUinteger,durAsUinteger)DimtAsDoubleDimAsUshortfixed_freq=1193181\freqAsmmovdx,&H61' turn speaker oninal,dxoral,&H03outdx,almovdx,&H43' get the timer readymoval,&HB6outdx,almovax,wordPtr[fixed_freq]' move freq to axmovdx,&H42' port to outoutdx,al' out low orderxchgah,aloutdx,al' out high orderEndAsmt=TimerWhile((Timer-t)*1000)<dur' wait for out specified durationSleep(1)WendAsmmovdx,&H61' turn speaker offinal,dxandal,&HFCoutdx,alEndAsmEndSubSubSound(ByvalfreqAsUinteger,durAsUinteger)#ifndef__fb_win32__' If not windows Then call the asm version.Sound_DOS_LIN(freq,dur)#Else' If WindowsDimosvAsOSVERSIONINFOosv.dwOSVersionInfoSize=Sizeof(OSVERSIONINFO)GetVersionEx(@osv)SelectCaseosv.dwPlatformIdCaseVER_PLATFORM_WIN32_NT' If NT then use Beep from APIBeep_(freq,dur)CaseElse' If not on NT then use the same as DOS/LinuxSound_DOS_LIN(freq,dur)EndSelect#endifEndSub'----------Submetronome()DimAsDoublebpm=120.0' beats por minutoDimAsLongretardo=1000*(60.0/bpm)DimAsIntegerbpb=4' beats por compásDimAsIntegercounter=0' contador internoDocounter+=1IfcounterModbpb<>0ThenSound(100,60)Color10:Print"tick ";ElseSound(119,60)Color11:Print"TICK "EndIfSleep(retardo)LoopEndSub

FutureBasic

An Apple Mac application that, when compiled with FB, produces a packaged, stand-alone 64-bit application that will run on either Intel or the newer M-series Macs. It's GUI includes a slider control to adjust speed, as well as blinking indicator and a sound, to indicate tempo. The code has been tested on Catalina (10.15.x) to Monterey (12.4.x) with Ventura still in beta at the time of this post. The free FB compiler is available on theFutureBasic home page.

/* Basic Tempo Markings from slowest to fastest: • Larghissimo – very, very slow (24 bpm and under) • Grave – very slow (25–45 bpm) • Largo – broadly (40–60 bpm) • Lento – slowly (45–60 bpm) • Larghetto – rather broadly (60–66 bpm) • Adagio – slow & (literally (66–76 bpm) • Adagietto – slower than andante (72–76 bpm) • Andante – walking pace (76–108 bpm) • Andantino – slightly faster (80–108 bpm) • Marcia moderato – moderately, a march (83–85 bpm) • Andante moderato – between andante and moderato (92–112 bpm) • Moderato – moderately (108–120 bpm) • Allegretto – moderately fast (112–120 bpm) • Allegro moderato – not quite allegro (116–120 bpm) • Allegro – fast, quick, bright (120–168 bpm) • Vivace – lively and fast (168–176 bpm) • Vivacissimo – very fast and lively (172–176 bpm) • Allegrissimo – very fast (172–176 bpm) • Presto – very, very fast (168–200 bpm) • Prestissimo – even faster (200 bpm and over) */ output file "Metronome" include "Tlbx AVFoundation.incl" // Uncomment next line to use external sound file, and make necessary adjustments in fn RunMetronome// include resources "toc.wav" begin enum _mApplication _mFile _mEdit _mColor end enum begin enum 1 _iSeparator _iPreferences end enum begin enum _iClose end enum _window = 1 begin enum 1 _slider = 30 _bpmIndicator end enum _initialBPM = 100 void local fn BuildMenus '~'1 // application menu _mApplication, _iSeparator menu _mApplication, _iPreferences,, @"Preferences…", @"," // file menu _mFile, -1,, @"File" menu _mFile, _iClose,, @"Close", @"w" MenuItemSetAction( _mFile, _iClose, @"performClose:" ) editmenu _mEdit end fn void local fn BuildWindow '~'1 NSUInteger i CGRect r = fn CGRectMake( 0, 0, 200, 500 ) window _window, @"Metronome", r, NSWindowStyleMaskTitled + NSWindowStyleMaskClosable + NSWindowStyleMaskMiniaturizable r = fn CGRectMake( 160, 20, 32, 465 ) slider _slider, YES, _initialBPM, r, 20, 190, _window r = fn CGRectMake( 25, 458, 24, 24 ) colorwell _bpmIndicator, YES, fn ColorGreen, r, NO, _window ColorWellSetBordered( _bpmIndicator, NO ) CFArrayRef tempo = @[¬ @"Prestissimo", @"Presto", @"Allegrissimo", @"Vivacissimo", @"Vivace",¬ @"Allegro", @"Allegro moderato", @"Allegretto", @"Moderato", @"Andante moderato",¬ @"Marcia moderato", @"Andantino", @"Andante", @"Adagietto", @"Adagio", @"Larghetto",¬ @"Lento", @"Largo", @"Grave", @"Larghissimo"] r = fn CGRectMake( 10, 460, 140, 22 ) for i = 1 to 20 textlabel i, tempo[i-1], r, _window ControlSetAlignment( i, NSTextAlignmentRight ) ControlSetFontWithName( i, @"Menlo", 11.5 ) r = fn CGRectOffset( r, 0, -23.2 ) next end fn local fn StopTimer '~'1 CFRunLoopTimerRef t = (CFRunLoopTimerRef)fn AppProperty( @"timer" ) if ( fn TimerIsValid( t ) ) TimerInvalidate( t ) ColorWellSetColor( _bpmIndicator, fn ColorGreen ) end if end fn local fn RunMetronome( bpm as CFTimeInterval ) '~'1 // Uncomment these lines to use an external sound file name toc.wave// CFURLRef soundURL = fn BundleURLForSoundResource( fn BundleMain, @"toc.wav" )// SoundRef tocSound = fn SoundWithContentsOfURL( soundURL, NO ) // Comment this line out to use external sound file for tock soundSoundRef tocSound = fn SoundNamed( @"Pop" ) CFTimeInterval interval = 60.0 / bpm CFRunLoopTimerRef tocTimer = timerbegin 0.0, interval, YES ColorRef color if ( fn ObjectIsEqual( fn ColorWellColor( _bpmIndicator ), fn ColorGreen ) ) color = fn ColorGray ColorWellSetColor( _bpmIndicator, color ) else color = fn ColorGreen ColorWellSetColor( _bpmIndicator, color ) end if fn SoundStop( tocSound ) fn SoundPlay( tocSound ) timerend AppSetProperty( @"timer", tocTimer ) end fn void local fn DoAppEvent( ev as long ) '~'1 select (ev) case _appDidFinishLaunching fn BuildMenus fn BuildWindow fn RunMetronome( _initialBPM ) case _appShouldTerminateAfterLastWindowClosed AppEventSetBool(YES) end select end fn void local fn DoMenu( menuID as long, itemID as long ) '~'1 select (menuID) case _mApplication select (itemID) case _iPreferences end select end select end fn void local fn DoDialog( ev as long, tag as long, wnd as long ) '~'1 select ( ev ) case _btnClick select ( tag ) case _slider : fn StopTimer : fn RunMetronome( fn ControlIntegerValue( tag ) ) end select end select end fn on AppEvent fn DoAppEvent on menu fn DoMenu on dialog fn DoDialog HandleEvents
Output:
[Since this code generates a standalone 64-bit Macintosh application with its own window and controls, it has to be compiled with FB to see the GUI and test the output.]

Go

As with theRaku example, just simple text output.It would be reasonably simple (but covered better in other tasks)to change bpm and bpb into command line arguments,make it a function/object,and/or substitute sound production instead of text output.

time.Ticker's documentation says that it"adjusts the intervals or drops ticks to make up for slow receivers".So, as long as the output or sound production finishes before the next tick,the timing will be reliable and will not drift which is the gist of this task.

packagemainimport("fmt""time")funcmain(){varbpm=72.0// Beats Per Minutevarbpb=4// Beats Per Bard:=time.Duration(float64(time.Minute)/bpm)fmt.Println("Delay:",d)t:=time.NewTicker(d)i:=1for_=ranget.C{i--ifi==0{i=bpbfmt.Printf("\nTICK ")}else{fmt.Printf("tick ")}}}
Output:
Delay: 833.333333msTICK tick tick tick TICK tick tick tick TICK tick ^C

Haskell

Works with:GHC version 7.4.2
importControl.ConcurrentimportControl.Concurrent.MVarimportSystem.Process(runCommand)-- This program works only on the GHC compiler because of the use of-- threadDelaydataBeep=Stop|Hi|LowtypePattern=[Beep]typeBeatsPerMinute=Intminute=60000000-- 1 minute = 60,000,000 microseconds-- give one of the following example patterns to the metronome functionpattern4_4=[Hi,Low,Low,Low]pattern2_4=[Hi,Low]pattern3_4=[Hi,Low,Low]pattern6_8=[Hi,Low,Low,Low,Low,Low]-- use this version if you can't play audio, use Windows or don't-- have audio files to play-- beep :: Beep -> IO ()-- beep Stop = return ()-- beep Hi = putChar 'H'-- beep Low = putChar 'L'-- use this version if you can and want to play audio on Linux using-- Alsa. Change the name of the files to those of your choicebeepStop=return()beepHi=putChar'H'>>runCommand"aplay hi.wav &> /dev/null">>return()beepLow=putChar'L'>>runCommand"aplay low.wav &> /dev/null">>return()tick::MVarPattern->BeatsPerMinute->IO()tickbi=dot<-readMVarbcasetof[Stop]->return()x->domapM_(\v->forkIO(beepv)>>threadDelay(minute`div`i))ttickbimetronome::Pattern->BeatsPerMinute->IO()metronomepi=doputStrLn"Press any key to stop the metronome."b<-newMVarp_<-forkIO$tickbi_<-getCharputMVarb[Stop]

HTML /CSS /JavaScript

Synthesised audio via the WebAudio API with a simple sinewave->envelope->bandpass audio chain, visual indication is provided by animating box shadows in CSS.Try the Metronome out here.

<!DOCTYPE html><html><head><metacharset='utf-8'><title>Metronome</title><metaname='viewport'content='width=device-width, initial-scale=1'><style>:root{color-scheme:darklight;font-family:sans-serif;--base-color:red;}body{display:flex;justify-content:center;flex-direction:column;margin:4ch;font-size:x-large;}button{padding:2ch4ch;margin:2ch;width:100%;font-size:xx-large;}output{min-width:8ch;align-self:end;text-align:right;}label{display:flex;flex-direction:column;margin:1ch;}div{display:flex;}input[type=color]{width:20%;}</style></head><body><main><label>Volume<outputid="volume-output">70%</output><inputid="volume-input"type="range"min="0"max="100"step="1"value="70"/></label><label>Tempo<outputid="bpm-output">120 bpm</output><inputid="bpm-input"type="range"min="0"max="100"step="any"value="50"/></label><label>Subdivisions<outputid="bpb-output">4 beats</output><inputid="bpb-input"type="range"min="1"max="15"step="1"value="4"/></label><div><buttonid="start">Start</button><buttonid="stop">Stop</button><inputid="color"type="color"value="#ff0000"/></div></main><script>//@ts-check//@ts-ignoreconst/** @type {HTMLInputElement}  */volumeInput=document.getElementById('volume-input')//@ts-ignoreconst/** @type {HTMLOutputElement} */volumeOutput=document.getElementById('volume-output')//@ts-ignoreconst/** @type {HTMLInputElement}  */bpmInput=document.getElementById('bpm-input')//@ts-ignoreconst/** @type {HTMLOutputElement} */bpmOutput=document.getElementById('bpm-output')//@ts-ignoreconst/** @type {HTMLInputElement}  */bpbInput=document.getElementById('bpb-input')//@ts-ignoreconst/** @type {HTMLOutputElement} */bpbOutput=document.getElementById('bpb-output')//@ts-ignoreconst/** @type {HTMLButtonElement} */startButton=document.getElementById('start')//@ts-ignoreconst/** @type {HTMLButtonElement} */stopButton=document.getElementById('stop')//@ts-ignoreconst/** @type {HTMLInputElement} */colorPicker=document.getElementById('color')//@ts-ignoreconst/** @type {HTMLElement} */mainElement=document.querySelector('main')let/** @type {AudioContext} */contextlet/** @type {GainNode} */gainlet/** @type {number?} */callback=nullletbpb=4letbpm=120lethighOrLow=0bpmInput.addEventListener('input',()=>{bpm=60+Math.max(0,Math.min(240,Math.round(0.024*Number(bpmInput.value)**2)))bpmOutput.innerText=bpm.toString()+' bpm'})volumeInput.addEventListener('input',()=>{volumeOutput.innerText=volumeInput.value+'%'})bpbInput.addEventListener('input',()=>{bpb=Number(bpbInput.value)bpbOutput.innerText=bpbInput.value+' beats'})stopButton.addEventListener('click',()=>{if(callback!==null){clearTimeout(callback)callback=nullhighOrLow=0}})colorPicker.addEventListener('input',()=>{document.body.style.setProperty('--base-color',colorPicker.value)})functionbeep(){constosc=newOscillatorNode(context,{channelCount:1,frequency:highOrLow===0?880:880*2/3})constenvelope=newGainNode(context,{gain:0})osc.connect(envelope).connect(gain)envelope.gain.exponentialRampToValueAtTime(1,0.01)envelope.gain.setTargetAtTime(0,context.currentTime+0.1,0.08)osc.start()osc.stop(context.currentTime+1)highOrLow=(highOrLow+1)%bpbconsttime=60000/bpmcallback=setTimeout(beep,time)document.body.animate([{boxShadow:'0 0 5vw 5vw var(--base-color)',easing:'ease-out'},{}],time)mainElement.animate([{boxShadow:'inset 0 0 5vw 5vw var(--base-color)',easing:'ease-out'},{}],time)}startButton.addEventListener('click',()=>{if(!context){context=newAudioContext({latencyHint:'balanced'})gain=newGainNode(context,{gain:Math.max(1,Number(volumeInput.value))})gain.gain.value=(Number(volumeInput.value)/100)**2volumeInput.addEventListener('input',()=>{gain.gain.value=(Number(volumeInput.value)/100)**2})gain.connect(newBiquadFilterNode(context,{type:'bandpass',frequency:1200})).connect(context.destination)}if(callback===null){beep()}})</script></body></html>

J

The explicit version (nbars,t) MET (barlengths;bpm) prints a bell character every beat, accompanied by a sequence of slashes, spaces, and backspaces to create a little animation. It includes a beat hand and a measure (or bar) hand.
MET can take several barlengths and bpm values, in which case it will cycle through them individually at each measure, creating (perhaps) interesting patterns. It will stop when nbars measures have been cycled through, or at the end of the current measure if the time limit is exceeded. The clock is self correcting.
MET returns the total number of measures, beats, and elapsed time.
If you leave out the left arguments, it will set them to infinity, so you can go insane without worrying about the metronome ever stopping.

MET=:__&$::(4 : 0)'BEL BS LF CR'=.781013{a.'`print stime delay'=.1!:2&4`(6!:1)`(6!:3)ticker=.22$'\  /''small large'=.(BEL,2#BS);5#BSclrln=.CR,(79#' '),CRx=.2({.,)xy=._1|.&.>2({.,)y'i j'=.0print'bpb \  bpm \ ',2#BSdelay1x=.({.,('ti t'=.stime'')+{:)xwhile.x*./@:>i,tdo.'bpb bpm'=.{.@>y=.1|.&.>ydl=.60%bpmprintclrln,(":bpb),' ',(ticker{~2|i=.>:i),' ',(":bpm),' 'for.i.bpbdo.printsmall,~ticker{~2|j=.>:jdelay0>.(t=.t+dl)-stime''end.end.printclrlni,j,t-ti)NB. Basic tacit version; this is probably considered bad coding style. At least I removed the "magic constants". Sort of.NB. The above version is by far superior.'BEL BS LF'=:7810{a.'`print delay'=:1!:2&4`(6!:3)met=:_&$::((]({:@][LFprint@[(-.@{.@][delay@[print@](BEL,2#BS),(22$'\  /'){~{.@])^:({:@]))1,<.@%)60&%[print@('\ '"_))
Output:
   16 60 MET 4;1204  / 120  /   NB. Variable measure lengths, and corresponding bpm:   21 _ MET 4 3 4 5 ; 120 100 120 150    NB. _ is infinity.5 \  150  /   NB. returns: 21 84 39.2    (21 measures, 84 beats, 39.2 seconds)   MET 4 8;120 240    NB. It can almost make music!bpb \  bpm \

Java

classMetronome{doublebpm;intmeasure,counter;publicMetronome(doublebpm,intmeasure){this.bpm=bpm;this.measure=measure;}publicvoidstart(){while(true){try{Thread.sleep((long)(1000*(60.0/bpm)));}catch(InterruptedExceptione){e.printStackTrace();}counter++;if(counter%measure==0){System.out.println("TICK");}else{System.out.println("TOCK");}}}}publicclasstest{publicstaticvoidmain(String[]args){Metronomemetronome1=newMetronome(120,4);metronome1.start();}}

AudioVisual

This example provides an audible and visual indications of the metronome timing.

It uses a timing loop to ensure that its timing is reliable in the long term.

importjava.awt.Color;importjava.awt.Dimension;importjava.awt.EventQueue;importjava.io.IOException;importjava.net.URL;importjava.util.concurrent.Executors;importjava.util.concurrent.ScheduledExecutorService;importjava.util.concurrent.TimeUnit;importjavax.sound.sampled.AudioInputStream;importjavax.sound.sampled.AudioSystem;importjavax.sound.sampled.Clip;importjavax.sound.sampled.LineUnavailableException;importjavax.sound.sampled.UnsupportedAudioFileException;importjavax.swing.ImageIcon;importjavax.swing.JFrame;importjavax.swing.JPanel;publicfinalclassMetronomeTask{publicstaticvoidmain(String[]aArgs){EventQueue.invokeLater(()->{newMetronome(60,4,1).start();});}}finalclassMetronomeextendsJPanel{publicMetronome(intaBeatsPerMinute,intaMeasure,intaDurationInMinutes){beatsPerMinute=aBeatsPerMinute;measure=aMeasure;durationInMinutes=aDurationInMinutes;SoundEffect.initialise();createAndShowGUI();}publicvoidstart(){executorService=Executors.newSingleThreadScheduledExecutor();executorService.scheduleAtFixedRate(provideService,1,1,TimeUnit.SECONDS);}privatevoidcreateAndShowGUI(){JFrame.setDefaultLookAndFeelDecorated(true);frame=newJFrame("Metronome");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);frame.setIconImage(newImageIcon("./metronomeJava.png").getImage());frame.add(createPanel());frame.pack();frame.setLocationRelativeTo(null);frame.setResizable(false);frame.setVisible(true);}privateJPanelcreatePanel(){setPreferredSize(newDimension(800,600));setBackground(Color.CYAN);returnthis;}privateJFrameframe;privateScheduledExecutorServiceexecutorService;privateintbeatsPerMinute,measure,durationInMinutes,counter;privateRunnableprovideService=()->{if(counter<durationInMinutes*beatsPerMinute){counter++;if(counter%measure!=0){SoundEffect.Tick.play();if(getBackground()!=Color.PINK){setBackground(Color.PINK);}else{setBackground(Color.CYAN);}}else{SoundEffect.Tock.play();setBackground(Color.ORANGE);}}else{executorService.shutdown();frame.dispose();Runtime.getRuntime().exit(0);}};}enumSoundEffect{Tick("./metronomeTickJava.wav"),Tock("./metronomeTockJava.wav");publicstaticvoidinitialise(){values();}publicvoidplay(){if(clip.isRunning()){clip.stop();}clip.setFramePosition(0);clip.start();}privateSoundEffect(StringsoundFileName){URLurl=getClass().getClassLoader().getResource(soundFileName);try(AudioInputStreamaudioInputStream=AudioSystem.getAudioInputStream(url)){clip=AudioSystem.getClip();clip.open(audioInputStream);}catch(IOException|LineUnavailableException|UnsupportedAudioFileExceptionex){ex.printStackTrace();}}privateClipclip;}

Julia

Works with:Julia version 0.6
functionmetronome(bpm::Real=72,bpb::Int=4)s=60.0/bpmcounter=0whiletruecounter+=1ifcounter%bpb!=0println("tick")elseprintln("TICK")endsleep(s)endend

Kotlin

// version 1.1.2funmetronome(bpm:Int,bpb:Int,maxBeats:Int=Int.MAX_VALUE){valdelay=60_000L/bpmvarbeats=0do{Thread.sleep(delay)if(beats%bpb==0)print("\nTICK ")elseprint("tick ")beats++}while(beats<maxBeats)println()}funmain(args:Array<String>)=metronome(120,4,20)// limit to 20 beats
Output:
TICK tick tick tick TICK tick tick tick TICK tick tick tick TICK tick tick tick TICK tick tick tick

Liberty BASIC

Requires two supplied wav files for accentuated & standard sounds.

    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$)    waitsub quit w$    close #w$    endend 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

Locomotive Basic

Translation of:Python
10bpm=120:bpb=420sleep=50*60/bpm30counter=040everysleepgosub10050goto50100printcounter+1;" ";110ifcounter=0thenprint">TICK<":sound1,60,10,15elseprint" tick ":sound1,119,10120counter=counter+1130ifcounter=bpbthencounter=0140return

Mathematica /Wolfram Language

Shows/plays a visual and auditory metronome:

s=Sound[Play[Sin[1000t],{t,0,0.05}]];ss=Sound[Play[Sin[2000t],{t,0,0.05}]];bpm=180;slp=60/bpm;color=White;i=0;Dynamic[Graphics[{color,Disk[]},ImageSize->50]]While[True,i=Mod[i+1,4,1];color={Green,Red,Darker@Red,Red,Darker@Red}[[i]];If[i==1,EmitSound[ss],EmitSound[s]];Pause[slp];]

Nim

Textual version only.

importosprocmetronome(tempo,pattern:Positive)=letdelay=60_000divtempo# In milliseconds.varbeats=0whiletrue:stdout.writeifbeatsmodpattern==0:"\nTICK"else:" tick"stdout.flushFileincbeatssleep(delay)metronome(72,4)

Perl

The moduleTime::HiRes provides sub-secondsleep. Text output only.

useTime::HiResqw(sleep gettimeofday);local$|=1;# autoflushmy$beats_per_minute=shift||72;my$beats_per_bar=shift||4;my$i=0;my$duration=60/$beats_per_minute;my$base_time=gettimeofday()+$duration;for(my$next_time=$base_time;;$next_time+=$duration){if($i++%$beats_per_bar==0){print"\nTICK";}else{print" tick";}sleep($next_time-gettimeofday());}

Sample run

Output:
$ metronome 60 6TICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tick^C

Graphical version

Visual only, unless you want to uncomment the bell call.

#!/usr/bin/perlusestrict;usewarnings;useTk;my$active=0;my$bpm=60;my$x=100;my$mw=MainWindow->new;$mw->title("Metronome");my$c=$mw->Canvas()->pack(-fill=>'both',-expand=>1);$mw->Scale(-orient=>'horizontal',-from=>30,-to=>150,-variable=>\$bpm,-label=>'Beats per Minute',)->pack(-fill=>'x');$mw->Button(-text=>'Exit',-command=>sub{$mw->destroy},)->pack(-side=>'right');$mw->Button(-text=>'Start / Pause',-command=>sub{$active^=1;tick()},)->pack(-side=>'right');$mw->bind('<Configure>'=>sub{$x=$c->width>>2});MainLoop;subtick{$activeorreturn;# $mw->bell;$x*=-1;$c->delete('all');$c->createLine($c->width>>1,$c->height,$c->width/2+$x,30,-width=>5);$mw->after(60_000/ $bpm /2=>\&tick);}

Phix

Library:Phix/pGUI
Library:Phix/online

You can run this onlinehere, with a few sizing/layout issues I could use a little help with, ideally w/o breaking anything else.

---- demo\rosetta\virtunome.exw----  Originally by ghaberek--  Translated from win32lib by Pete Lomax----  I will note that accuracy drops sharply above 5 beats per second.--withjavascript_semantics-- needs some work though, usual sizing stuff [DEV]                            -- NB: don't break Morse_code.exw when fixing this!includepGUI.eIhandledlg,frame_1,radio,frame_2,val_lbl,frame_3,bpm_lbl,spb_lbl,act_lbl,acc_lbl,onbtn,onoff,timersequencenotesfunctionrle_decode_image(sequencedata)-- (not my best work, may benefit from a rethink...)sequenceimg={}fori=1tolength(data)dosequencerle=data[i],line={},valintegerrpt=rle[1]forj=2tolength(rle)-1by2dointegerRGB=rle[j],count=rle[j+1]ifRGB=-1thenstringRGBs=IupGetGlobal("DLGBGCOLOR"){val}=scanf(RGBs,"%d %d %d")elsifRGB=0thenval=repeat(0,3)else?9/0endifval&=#FFline&=flatten(repeat(val,count))endforimg&=flatten(repeat(line,rpt))endforreturnimgendfunctionconstantWhole_note={{13,-1,32},{1,-1,12,0,9,-1,11},{1,-1,10,0,4,-1,3,0,6,-1,9},{1,-1,9,0,5,-1,4,0,6,-1,8},{1,-1,8,0,5,-1,6,0,5,-1,8},{2,-1,8,0,5,-1,6,0,6,-1,7},{1,-1,8,0,6,-1,5,0,6,-1,7},{1,-1,8,0,6,-1,5,0,5,-1,8},{1,-1,9,0,6,-1,3,0,5,-1,9},{1,-1,11,0,11,-1,10},{9,-1,32},},Half_note={{30,-1,21,0,1,-1,10},{1,-1,14,0,8,-1,10},{1,-1,12,0,10,-1,10},{1,-1,11,0,6,-1,3,0,2,-1,10},{1,-1,10,0,5,-1,5,0,2,-1,10},{1,-1,10,0,4,-1,5,0,3,-1,10},{1,-1,10,0,3,-1,5,0,4,-1,10},{1,-1,10,0,2,-1,5,0,5,-1,10},{1,-1,10,0,2,-1,3,0,6,-1,11},{1,-1,10,0,10,-1,12},{1,-1,11,0,7,-1,14}},Eigth_note={{2,-1,17,0,1,-1,14},{2,-1,17,0,2,-1,13},{1,-1,17,0,3,-1,12},{1,-1,17,0,4,-1,11},{1,-1,17,0,5,-1,10},{1,-1,17,0,6,-1,9},{1,-1,17,0,1,-1,1,0,5,-1,8},{1,-1,17,0,1,-1,3,0,4,-1,7},{1,-1,17,0,1,-1,4,0,4,-1,6},{1,-1,17,0,1,-1,5,0,3,-1,6},{1,-1,17,0,1,-1,6,0,3,-1,5},{1,-1,17,0,1,-1,7,0,2,-1,5},{1,-1,17,0,1,-1,7,0,3,-1,4},{2,-1,17,0,1,-1,8,0,2,-1,4},{4,-1,17,0,1,-1,9,0,1,-1,4},{3,-1,17,0,1,-1,8,0,1,-1,5},{6,-1,17,0,1,-1,14},{1,-1,11,0,7,-1,14},{1,-1,9,0,9,-1,14},{1,-1,8,0,10,-1,14},{1,-1,7,0,11,-1,14},{2,-1,6,0,12,-1,14},{2,-1,6,0,11,-1,15},{1,-1,6,0,10,-1,16},{1,-1,7,0,7,-1,18},{1,-1,9,0,2,-1,21}},Quarter_note={{30,-1,21,0,1,-1,10},{1,-1,15,0,7,-1,10},{1,-1,13,0,9,-1,10},{1,-1,12,0,10,-1,10},{1,-1,11,0,11,-1,10},{2,-1,10,0,12,-1,10},{2,-1,10,0,11,-1,11},{1,-1,10,0,10,-1,12},{1,-1,11,0,7,-1,14},{1,-1,13,0,2,-1,17}},Sixteenth_note={{2,-1,17,0,1,-1,14},{2,-1,17,0,2,-1,13},{1,-1,17,0,3,-1,12},{1,-1,17,0,4,-1,11},{1,-1,17,0,5,-1,10},{1,-1,17,0,6,-1,9},{1,-1,17,0,7,-1,8},{1,-1,17,0,2,-1,2,0,4,-1,7},{1,-1,17,0,2,-1,3,0,4,-1,6},{1,-1,17,0,2,-1,4,0,3,-1,6},{1,-1,17,0,3,-1,4,0,3,-1,5},{1,-1,17,0,4,-1,4,0,2,-1,5},{1,-1,17,0,5,-1,3,0,3,-1,4},{1,-1,17,0,6,-1,3,0,2,-1,4},{1,-1,17,0,7,-1,2,0,2,-1,4},{1,-1,17,0,1,-1,2,0,5,-1,1,0,2,-1,4},{1,-1,17,0,1,-1,4,0,6,-1,4},{1,-1,17,0,1,-1,5,0,5,-1,4},{1,-1,17,0,1,-1,6,0,4,-1,4},{2,-1,17,0,1,-1,8,0,2,-1,4},{1,-1,17,0,1,-1,9,0,1,-1,4},{4,-1,17,0,1,-1,9,0,2,-1,3},{2,-1,17,0,1,-1,9,0,1,-1,4},{1,-1,11,0,7,-1,8,0,2,-1,4},{1,-1,9,0,9,-1,8,0,1,-1,5},{1,-1,8,0,10,-1,8,0,1,-1,5},{1,-1,7,0,11,-1,14},{2,-1,6,0,12,-1,14},{2,-1,6,0,11,-1,15},{1,-1,6,0,10,-1,16},{1,-1,7,0,7,-1,18},{1,-1,9,0,2,-1,21}}integernote=4-- quarter initiallyatomvLastTime=0.0-- for time resolutionconstant-- in bpmMIN_TEMPO=1,DEF_TEMPO=90,MAX_TEMPO=200integervMSPB=667-- default milliseconds per beatconstantTempos={"Grave","Largo","Adagio","Lento","Adante","Moderato","Allegretto","Allegro","Presto","Vivance","Prestissimo"}functionset_tempo(integerpBPM,atompNote)-- returns tempo indexintegerindex=floor(((length(Tempos)-1)*pBPM)/(MAX_TEMPO-MIN_TEMPO))+1atomlSPB=60/pBPM/pNote-- seconds per beatvMSPB=floor(lSPB*1000)IupSetStrAttribute(spb_lbl,"TITLE","%.2f",{lSPB})IupSetInt(timer,"TIME",vMSPB)ifIupGetInt(timer,"RUN")then-- restart needed to apply new TIME (not doc?)IupSetInt(timer,"RUN",false)IupSetInt(timer,"RUN",true)endifreturnindexendfunctionproceduretempo_change()integerlBPM=IupGetInt(val_lbl,"TITLE"),lIndex=set_tempo(lBPM,note/4)IupSetStrAttribute(frame_2,"TITLE","Tempo: %s ",{Tempos[lIndex]})vLastTime=time()endprocedurefunctiontoggle_state_cb(Ihandleih,integerstate)ifstatethennote=power(2,find(ih,notes)-1)-- 1/2/4/8/16tempo_change()endif-- and shift focus away, since it looks ugly w/o any textIupSetFocus(onbtn)returnIUP_DEFAULTendfunctionfunctionvaluechanged_cb(Ihandleval)integerv=IupGetInt(val,"VALUE")IupSetInt(val_lbl,"TITLE",v)IupSetStrAttribute(bpm_lbl,"TITLE","%.2f",{v})tempo_change()returnIUP_DEFAULTendfunctionincludebuiltins\beep.efunctiontimer_cb(Ihandle/*ih*/)beep(#200,20)atomlThisTime=time()ifvLastTime>0.0thenatomlDiff=(lThisTime-vLastTime),lResolution=((lDiff*1000)/vMSPB)*100IupSetStrAttribute(act_lbl,"TITLE","%0.2f",{lDiff})IupSetStrAttribute(acc_lbl,"TITLE","%d%%",{lResolution})endifvLastTime=lThisTimereturnIUP_DEFAULTendfunctionfunctionbutton_cb(Ihandleih)boolactive=notIupGetInt(timer,"RUN")IupSetInt(timer,"RUN",active)IupSetAttribute(ih,"TITLE",{"Off","On"}[active+1])returnIUP_DEFAULTendfunctionIupOpen()notes={IupImageRGBA(32,32,rle_decode_image(Whole_note)),IupImageRGBA(32,40,rle_decode_image(Half_note)),IupImageRGBA(32,40,rle_decode_image(Quarter_note)),IupImageRGBA(32,40,rle_decode_image(Eigth_note)),IupImageRGBA(32,40,rle_decode_image(Sixteenth_note))}sequencebtns={}fori=1tolength(notes)doIhandlebtn=IupToggle(NULL,Icallback("toggle_state_cb"),"CANFOCUS=NO"),lbl=IupLabel()IupSetAttributeHandle(lbl,"IMAGE",notes[i])btns&={btn,lbl}notes[i]=btnendforradio=IupRadio(IupHbox(btns,"GAP=20"))frame_1=IupFrame(radio,"MARGIN=20x10")IupSetAttribute(frame_1,"TITLE","Note ")val_lbl=IupLabel(" 200","ALIGNMENT=ARIGHT")Ihandleval=IupValuator("HORIZONTAL","EXPAND=HORIZONTAL, CANFOCUS=NO")IupSetInt(val,"MIN",MIN_TEMPO)IupSetInt(val,"MAX",MAX_TEMPO)IupSetInt(val,"VALUE",DEF_TEMPO)IupSetCallback(val,"VALUECHANGED_CB",Icallback("valuechanged_cb"))frame_2=IupFrame(IupHbox({val_lbl,val}),`TITLE="Tempo: "`)bpm_lbl=IupLabel("90.00","ALIGNMENT=ARIGHT, EXPAND=HORIZONTAL")act_lbl=IupLabel("0.00","ALIGNMENT=ARIGHT, EXPAND=HORIZONTAL")spb_lbl=IupLabel("0.67","ALIGNMENT=ARIGHT, EXPAND=HORIZONTAL")acc_lbl=IupLabel("0%","ALIGNMENT=ARIGHT, EXPAND=HORIZONTAL")frame_3=IupFrame(IupHbox({IupVbox({IupHbox({IupLabel("Beats Per Minute:"),bpm_lbl}),IupHbox({IupLabel("Seconds Per Beat:"),spb_lbl})},"GAP=10,MARGIN=10x0"),IupVbox({IupHbox({IupLabel("Actual Seconds Per Beat:"),act_lbl}),IupHbox({IupLabel("Accuracy:"),acc_lbl})},"GAP=10,MARGIN=10x0")}),`TITLE="Statistics ",MARGIN=4x8`)onbtn=IupButton("On",Icallback("button_cb"),"PADDING=30x0")onoff=IupHbox({IupFill(),onbtn},"MARGIN=0x20")dlg=IupDialog(IupVbox({frame_1,frame_2,frame_3,onoff},"MARGIN=10x5, GAP=5"),--              `TITLE="Virtunome",RASTERSIZE=500x330`)`TITLE="Virtunome"`)IupShow(dlg)-- The TIME and RUN attributes are set dynamically:timer=IupTimer(Icallback("timer_cb"),vMSPB,active:=false)IupSetInt(val_lbl,"TITLE",DEF_TEMPO)IupSetAttributeHandle(radio,"VALUE",btns[5])tempo_change()ifplatform()!=JSthenIupMainLoop()IupClose()endif

PicoLisp

A short beep (440 Hz, 40 msec) is produced in a child process, while a "pendulum" is swinging left and right. Hitting any key will stop it.

(de metronome (Bpm)   (if (fork)      (let Pid @         (for Pendulum '(" /" . ("^H^H\\ " "^H^H /" .))            (tell Pid 'call "/usr/bin/beep" "-f" 440 "-l" 40)            (prin Pendulum)            (T (key (*/ 30000 Bpm)) (tell Pid 'bye)) )         (prinl) )      (wait) ) )

Test:

: (metronome 60) /-> NIL  # A key was hit

Pure Data

#N canvas 553 78 360 608 10;#X obj 20 20 cnv 15 320 140 empty empty empty 20 12 0 14 -228856 -66577 0;#X obj 20 190 cnv 15 320 36 empty empty empty 20 12 0 14 -233017 -66577 0;#X obj 67 30 vradio 20 1 0 6 empty beats empty 0 -8 0 10 -86277 -262144 -1 1;#X text 40 33 1/1;#X text 40 53 2/2;#X text 40 73 3/4;#X text 40 93 4/4;#X text 40 133 6/8;#X obj 67 167 + 1;#X floatatom 67 201 5 0 0 0 beats - -;#X obj 181 32 vsl 20 115 208 40 0 0 empty bpm empty 25 10 0 10 -86277 -262144 -1 5971 0;#X text 208 42 Larghetto 60-66;#X text 208 58 Adagio 66-76;#X text 208 74 Andante 76-108;#X text 208 90 Moderato 108-120;#X text 208 106 Allegro 120-168;#X text 208 122 Presto 168-200;#X text 208 138 Prestissimo 200-208;#X text 208 26 Largo 40-60;#X obj 181 167 int;#X floatatom 181 201 5 0 0 1 bpm - -;#X obj 149 246 expr 1000 / ($f1/60);#X obj 122 125 tgl 25 0 empty on on/off -4 -7 0 10 -261682 -86277 -86277 0 1;#X obj 122 270 metro;#X obj 122 291 int;#X obj 42 249 + 1;#X obj 52 275 mod;#X obj 122 312 moses 1;#X obj 122 347 bng 32 250 50 0 empty empty empty 17 7 0 10 -228856 -258113 -1;#X obj 161 347 bng 32 250 50 0 empty empty empty 17 7 0 10 -228856 -260097 -1;#X msg 81 399 1 2 \, 1 2 1 \, 0 3 2;#X obj 81 420 vline~;#X msg 200 399 1 2 \, 1 2 1 \, 0 3 2;#X obj 200 420 vline~;#X obj 20 420 osc~ 1400;#X obj 139 420 osc~ 1230;#X obj 65 455 *~;#X obj 184 455 *~;#X obj 116 559 dac~;#X obj 117 523 +~;#X obj 278 490 loadbang;#X msg 278 511 \; pd dsp 1 \; beats 1 \; bpm 120 \; on 1;#X connect 2 0 8 0;#X connect 8 0 9 0;#X connect 9 0 26 1;#X connect 10 0 19 0;#X connect 19 0 20 0;#X connect 20 0 21 0;#X connect 21 0 23 1;#X connect 22 0 23 0;#X connect 23 0 24 0;#X connect 24 0 25 0;#X connect 24 0 27 0;#X connect 25 0 26 0;#X connect 26 0 24 1;#X connect 27 0 28 0;#X connect 27 1 29 0;#X connect 28 0 30 0;#X connect 29 0 32 0;#X connect 30 0 31 0;#X connect 31 0 36 1;#X connect 32 0 33 0;#X connect 33 0 37 1;#X connect 34 0 36 0;#X connect 35 0 37 0;#X connect 36 0 39 0;#X connect 37 0 39 1;#X connect 39 0 38 0;#X connect 39 0 38 1;#X connect 40 0 41 0;

PureBasic

PureBasic output
StructureMETRONOMEsmsPerBeat.iBeatsPerMinute.iBeatsPerCycle.ivolume.icanvasGadget.iw.ih.ioriginX.ioriginY.iradius.iactivityStatus.iEndStructureEnumeration;gadgets#TEXT_MSPB;millisecondsperbeat#STRING_MSPB;millisecondsperbeat#TEXT_BPM;beatsperminute#STRING_BPM;beatsperminute#TEXT_BPC;beatspercycle#STRING_BPC;beatspercycle#BUTTON_VOLM;volume-#BUTTON_VOLP;volume+#BUTTON_START;start#SPIN_BPM#CANVAS_METRONOMEEndEnumerationEnumeration;sounds#SOUND_LOW#SOUND_HIGHEndEnumeration#WINDOW=0;windowProcedurehandleError(Value,text.s)IfNotValue:MessageRequester("Error",text):End:EndIfEndProcedureProceduredrawMetronome(*m.METRONOMEs,Angle.f,cycleCount=0)ProtectedCircleX,CircleY,circleColorIfStartDrawing(CanvasOutput(*m\canvasGadget))Box(0,0,*m\w,*m\h,RGB(0,0,0))CircleX=Int(*m\radius*Cos(Radian(Angle)))CircleY=Int(*m\radius*Sin(Radian(Angle)))IfAngle=90IfcycleCount:circleColor=RGB(255,0,0):Else:circleColor=RGB(0,255,0):EndIfLineXY(*m\originX,*m\originY,*m\originX,*m\originY-CircleY,RGB(255,255,0))Circle(*m\originX+CircleX,*m\originY-CircleY-*m\radius*0.15,10,circleColor)ElseLineXY(*m\originX,*m\originY-*m\radius*1.02,*m\originX,*m\originY-*m\radius,RGB(255,255,0))LineXY(*m\originX,*m\originY,*m\originX+CircleX,*m\originY-CircleY,RGB(255,255,0))EndIfStopDrawing()ProcedureReturn1EndIfEndProcedureProcedure.iMetronome(*m.METRONOMEs)Protectedmilliseconds=Int((60*1000)/*m\BeatsPerMinute)ProtectedmsPerFrame,framesPerBeatProtectedi,j,cycleCount,startTime,frameEndTime,delayTime,delayError,h.f;calculatemetronomeanglesforeachframeofanimationIf*m\BeatsPerMinute<60framesPerBeat=Round(milliseconds/150,#PB_Round_Nearest)ElseframesPerBeat=Round((*m\BeatsPerMinute-420)/-60,#PB_Round_Nearest)EndIfIfframesPerBeat<1framesPerBeat=1DimmetronomeFrameAngle.f(1,framesPerBeat)metronomeFrameAngle(0,1)=90metronomeFrameAngle(1,1)=90ElseDimmetronomeFrameAngle.f(1,framesPerBeat*2)Forj=1ToframesPerBeath=45/framesPerBeatmetronomeFrameAngle(0,j)=90-h*(j-1)metronomeFrameAngle(0,framesPerBeat+j)=45+h*(j-1)metronomeFrameAngle(1,j)=90+h*(j-1)metronomeFrameAngle(1,framesPerBeat+j)=135-h*(j-1)NextframesPerBeat*2EndIfmsPerFrame=milliseconds/framesPerBeatPlaySound(#SOUND_HIGH)startTime=ElapsedMilliseconds()RepeatFori=0To1frameEndTime=startTime+msPerFrameForj=1ToframesPerBeatdrawMetronome(*m,metronomeFrameAngle(i,j),cycleCount);checkforthreadexitIf*m\activityStatus<0*m\activityStatus=0ProcedureReturnEndIfdelayTime=frameEndTime-ElapsedMilliseconds()If(delayTime-delayError)>=0Delay(frameEndTime-ElapsedMilliseconds()-delayError);waittheremainderofframeElseIfdelayTime<0delayError=-delayTimeEndIfframeEndTime+msPerFrameNext;checkforthreadexitIf*m\activityStatus<0*m\activityStatus=0ProcedureReturnEndIfWhile(ElapsedMilliseconds()-startTime)<milliseconds:WendSetGadgetText(*m\msPerBeat,Str(ElapsedMilliseconds()-startTime))cycleCount+1:cycleCount%*m\BeatsPerCycleIfcycleCount=0PlaySound(#SOUND_HIGH)ElsePlaySound(#SOUND_LOW)EndIfstartTime+millisecondsNextForEverEndProcedureProcedurestartMetronome(*m.METRONOMEs,MetronomeThread);startupthethreadwithnewvalues*m\BeatsPerMinute=Val(GetGadgetText(#STRING_BPM))*m\BeatsPerCycle=Val(GetGadgetText(#STRING_BPC))*m\activityStatus=1If*m\BeatsPerMinuteMetronomeThread=CreateThread(@Metronome(),*m)EndIfProcedureReturnMetronomeThreadEndProcedureProcedurestopMetronome(*m.METRONOMEs,MetronomeThread);ifthethreadisrunning:stopitIfIsThread(MetronomeThread)*m\activityStatus=-1;signalthreadtostopEndIfdrawMetronome(*m,90)EndProcedureDefinew=360,h=360,ourMetronome.METRONOMEs;initializethemetronomeWithourMetronome\msPerBeat=#STRING_MSPB\canvasGadget=#CANVAS_METRONOME\volume=10\w=w\h=h\originX=w/2\originY=h/2\radius=100EndWithourMetronome\canvasGadget=#CANVAS_METRONOME;initializesoundshandleError(InitSound(),"Sound system is Not available")handleError(CatchSound(#SOUND_LOW,?sClick,?eClick-?sClick),"Could Not CatchSound")handleError(CatchSound(#SOUND_HIGH,?sClick,?eClick-?sClick),"Could Not CatchSound")SetSoundFrequency(#SOUND_HIGH,50000)SoundVolume(#SOUND_LOW,ourMetronome\volume)SoundVolume(#SOUND_HIGH,ourMetronome\volume);setupwindow&GUIDefineStyle,i,wp,ghStyle=#PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_MinimizeGadgethandleError(OpenWindow(#WINDOW,0,0,w+200+12,h+4,"Metronome",Style),"Not OpenWindow")SetWindowColor(#WINDOW,$505050)IfLoadFont(0,"tahoma",9,#PB_Font_HighQuality|#PB_Font_Bold)SetGadgetFont(#PB_Default,FontID(0))EndIfi=3:wp=10:gh=22TextGadget(#TEXT_MSPB,w+wp,gh*i,100,gh,"MilliSecs/Beat ",#PB_Text_Center)StringGadget(#STRING_MSPB,w+wp+108,gh*i,90,gh,"0",#PB_String_ReadOnly):i+2TextGadget(#TEXT_BPM,w+wp,gh*i,100,gh,"Beats/Min ",#PB_Text_Center)StringGadget(#STRING_BPM,w+wp+108,gh*i,90,gh,"120",#PB_String_Numeric):i+2GadgetToolTip(#STRING_BPM,"Valid range is 20 -> 240")TextGadget(#TEXT_BPC,w+wp,gh*i,100,gh,"Beats/Cycle ",#PB_Text_Center)StringGadget(#STRING_BPC,w+wp+108,gh*i,90,gh,"4",#PB_String_Numeric):i+2GadgetToolTip(#STRING_BPC,"Valid range is 1 -> BPM")ButtonGadget(#BUTTON_START,w+wp,gh*i,200,gh,"Start",#PB_Button_Toggle):i+2ButtonGadget(#BUTTON_VOLM,w+wp,gh*i,100,gh,"-Volume")ButtonGadget(#BUTTON_VOLP,w+wp+100,gh*i,100,gh,"+Volume")CanvasGadget(ourMetronome\canvasGadget,0,0,ourMetronome\w,ourMetronome\h,#PB_Image_Border)drawMetronome(ourMetronome,90)Definemsg,GID,MetronomeThread,ValueRepeat;thecontrolloopforourapplicationmsg=WaitWindowEvent(1)GID=EventGadget()etp=EventType()IfGetAsyncKeyState_(#VK_ESCAPE):End:EndIf;removewhenappiso.k.SelectmsgCase#PB_Event_CloseWindowEndCase#PB_Event_GadgetSelectGIDCase#STRING_BPMIfetp=#PB_EventType_LostFocusValue=Val(GetGadgetText(#STRING_BPM))IfValue>390Value=390ElseIfValue<20Value=20EndIfSetGadgetText(#STRING_BPM,Str(Value))EndIfCase#STRING_BPCIfetp=#PB_EventType_LostFocusValue=Val(GetGadgetText(#STRING_BPC))IfValue>Val(GetGadgetText(#STRING_BPM))Value=Val(GetGadgetText(#STRING_BPM))ElseIfValue<1Value=1EndIfSetGadgetText(#STRING_BPC,Str(Value))EndIfCase#BUTTON_VOLP,#BUTTON_VOLM;changevolumeIfGID=#BUTTON_VOLPAndourMetronome\volume<100ourMetronome\volume+10ElseIfGID=#BUTTON_VOLMAndourMetronome\volume>0ourMetronome\volume-10EndIfSoundVolume(#SOUND_LOW,ourMetronome\volume)SoundVolume(#SOUND_HIGH,ourMetronome\volume)Case#BUTTON_START;thetogglebuttonforstart/stopSelectGetGadgetState(#BUTTON_START)Case1stopMetronome(ourMetronome,MetronomeThread)MetronomeThread=startMetronome(ourMetronome,MetronomeThread)SetGadgetText(#BUTTON_START,"Stop")Case0stopMetronome(ourMetronome,MetronomeThread)SetGadgetText(#BUTTON_START,"Start")EndSelectEndSelectEndSelectForEverEndDataSection;asmallwavfilesavedasrawdatasClick:Data.q$0000082E46464952,$20746D6645564157,$0001000100000010,$0000AC440000AC44Data.q$6174616400080001,$8484848300000602,$8B8A898886868585,$C0B3A9A29C95918EData.q$31479BD3CED0CFCF,$3233323232323331,$BDAEC4C9A1423133,$D0CFCFD0CFD1CDD4Data.q$323232333770A9CB,$2E34313332333232,$CFD0CFCACFCFAF53,$9783AAD3CED0CFD0Data.q$3233313332448AA1,$4430333233323233,$CFD0CFAE7D7E9483,$B7B9C3B8BFD1CED0Data.q$3233313037476898,$3E48493D32333233,$85959187775C4338,$898A8F8D807A7978Data.q$737F898381888D8D,$3131435564717A77,$332F36413A343234,$827B7873695C4D37Data.q$9C9B949191908F8A,$4D50515A67758694,$5451484341414245,$7B83868782756559Data.q$565D5F616365676E,$7871675F57504B4E,$797C8083827E7C7B,$4D525E686C6D7176Data.q$4D4B4B474343464A,$8B82796F655D5953,$7B7C83888B909392,$5153595E666F767BData.q$5A5A5B5B59575553,$696C6E6D67615E5B,$7879777573726F6B,$71727376797C7B79Data.q$505352505159646C,$6B6153463C3B414A,$A09B908379706D6E,$6F767A7E858C949CData.q$4D4D4845484E5662,$80796F645B544E4C,$8487888885828283,$555A5D606369737DData.q$6A665F58524E4E51,$878E8F8B867D736D,$54606B72797F8081,$5852504F4E4E4C4DData.q$7E7B7A79756F675F,$6B6C6F757C7F8182,$6D6865676C6F6E6C,$6E6B6C7074777773Data.q$615E5D60676F7372,$7069636061636463,$81817F7C7A797976,$65676B6E72797E80Data.q$65625F5E5D5D5F62,$7D7C7B7875716E6A,$6F74777B7E7F7F7E,$454950565D63676AData.q$605A55504B464343,$9E9F9D978E817469,$6E7D8A93989A9B9C,$444546494C505861Data.q$7B756F665C534C46,$7E82858788888580,$5C5D61666B70757A,$6A6B6B6B6965615EData.q$646B717676736F6B,$727272716D676261,$8285878885817A74,$5F5F636A72797D80Data.q$645D58585A5E6060,$827D79777877746D,$7878797C80848685,$6A686664666B7075Data.q$76726C666364686B,$807E7B7878787878,$656A6F74797B7D7F,$59585A5C5D5F6163Data.q$84807C79746D665E,$777C81878B8C8B89,$555352555B636B72,$82776B625C595957Data.q$989B9A979493918B,$656A6E7277818A92,$535457575656585E,$6E6E6F6D675E5753Data.q$898C9398968D7F74,$69717E8C9697918B,$3B39414F5D656767,$695B56565A595145Data.q$8986878A8F90897B,$7A7875757B848A8C,$747168605E636D76,$7B7365585257636FData.q$8C8981777272777C,$70757676767A8188,$6A6D6D68625F6269,$8687847D746C6868Data.q$8485888A89868484,$585A616B747B8083,$5B555355595C5C5A,$8C898786847D7265Data.q$888C9096999A9892,$6163666C72797F83,$5E554E4B4C52595E,$91887C7169676663Data.q$8E8E8A88888C9193,$656666676A737E88,$655F585352555B62,$8B88837C756F6B69Data.q$7E858B8E8E8B898B,$62676B6D6D6C6E74,$6C6C6B6A6764605F,$7C7978787775736FData.q$8E8C8C8C8D8D8982,$686D747C858C9091,$625C58585B5E6265,$908A837C75706C68Data.q$848A8E9396989896,$545960676E757A7F,$65605E5D5B575352,$A19D9890877C746CData.q$8992979A9C9FA1A2,$525355575C64707E,$736D665D56514F50,$8D8D8B8986827E79Data.q$7777797B7F84898C,$78746F6A6A6E7376,$7B79767372747879,$6C6B69686A6F757AData.q$7C78746F6D6C6C6D,$888B8C8B88858280,$6F75797B7D7F8184,$6F6D6B686565676AData.q$8785817D79747270,$868B8D8D8C8A8988,$6F73777A7C7D7F82,$6F6B68656465666AData.q$8A837D7976757472,$76787A7D82888C8D,$6562606064696F73,$8E8A847C756F6B68Data.q$8D90939494929190,$606264686F788088,$73685D58585A5D5F,$9B96918C8987837DData.q$71767B838C959B9D,$5B5756585D63686D,$8C86817B756F6962,$888F939597979591Data.q$5C606366696F7780,$7A756D6259535458,$9EA2A0988D837E7C,$79858D8F8F8E9097Data.q$656B6E6D6865666E,$797A79746C656060,$7F7F7F7F7E7C7978,$8381828384848280Data.q$7F82888E92918D88,$59606A757C7F7F7F,$655E5A5A59585656,$A59F97918A82796EData.q$7E848A939CA6ABAA,$48454548515E6B76,$8A7C6E6057514E4B,$A9ACABA8A4A09B94Data.q$5A626A737F8C98A3,$60554D49484A4E54,$A9A19A928A81776B,$848C969FA7ADAFADData.q$4B494C525C67717B,$8A7D7167605A544F,$A2A4A5A6A6A49F96,$626970777F89949CData.q$6B655E56504F545A,$A19F988F8379736E,$848C9296989A9C9F,$61626465676B717AData.q$807E79736D676261,$86898B8C8A888583,$797A7E8284848484,$77777A7C7E7E7C7AData.q$7979797B7D7E7C79,$7D7C7B7B7C7C7B7A,$7D7D7C7B7A7B7C7D,$797978777677797BData.q$787A7A7976757678,$415380817C777576,$2C31000002005255,$30202C36202C3020eClick:EndDataSection

Python

#lang Pythonimporttimedefmain(bpm=72,bpb=4):sleep=60.0/bpmcounter=0whileTrue:counter+=1ifcounter%bpb:print'tick'else:print'TICK'time.sleep(sleep)main()

Racket

#langracket(requireracket/gui)(definemsec500)(definesounds'("hi.wav""lo.wav"))(definecolors'("red""green"))(definef(newframe%[label"Metronome"][width200][height200]))(definec(new(classcanvas%(definebrushes(map(λ(c)(newbrush%[colorc][style'solid]))colors))(definecur0)(define/override(on-paint)(send*(sendthisget-dc)(clear)(set-brush(list-refbrushescur))(draw-rectangle00200200)))(define/public(flip!)(set!cur(modulo(add1cur)(lengthsounds)))(play-sound(list-refsoundscur)#f)(on-paint))(super-new))[parentf]))(define(flip)(defineinit(current-inexact-milliseconds))(definenext(+msecinit))(defineticks1)(letloop()(when(>(current-inexact-milliseconds)next)(set!ticks(add1ticks))(set!next(+init(*msecticks)))(queue-callback(λ()(sendcflip!))))(sleep0.01)(loop)))(send*f(center)(show#t))(void(threadflip))

Raku

(formerly Perl 6)

This code only uses textual output, but any noise-generating commands may be substituted; as long as they are executed synchronously, and do not run longer than the specified duration, the timing loop will compensate, since the sequence operator is determining a list of absolute times for eachsleep to target.

subMAIN ($beats-per-minute =72,$beats-per-bar =4) {my$duration =60 /$beats-per-minute;my$base-time =now +$duration;my$i;for$base-time,$base-time +$duration ... * ->$next-time {if$i++ %%$beats-per-bar {print"\nTICK";        }else {print" tick";        }sleep$next-time -now;    }}

Sample run:

$ metronome 120 6TICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick^C

REXX

These REXX program examples are modeled after the Raku example.

textual visual, no sound

/*REXX program simulates a visual (textual)  metronome  (with no sound).                */parseargbpmbpbdur./*obtain optional arguments from the CL*/ifbpm==''|bpm==","thenbpm=72/*the number of  beats  per minute.    */ifbpb==''|bpb==","thenbpb=4/* "     "    "    "     "   bar.      */ifdur==''|dur==","thendur=5/*duration of the  run  in seconds.    */calltime'Reset'/*reset the REXX elapsed timer.        */bt=1/bpb/*calculate a   tock-time   interval.  */dountilet>=dur;et=time('Elasped')/*process  tick-tocks  for the duration*/say;callcharout,'TICK'/*show the first tick for the period.  */es=et+1/*bump the elapsed time  "limiter".    */$t=et+btdountile>=es;e=time('Elapsed')ife<$ttheniterate/*time for tock?     */callcharout,' tock'/*show a  "tock".    */$t=$t+bt/*bump the TOCK time.*/end/*until e≥es*/end/*until et≥dur*//*stick a fork in it,  we're all done. */

output   when using the default inputs:<per>TICK tock tock tock tockTICK tock tock tock tockTICK tock tock tock tockTICK tock tock tock tockTICK tock tock tock tockTICK tock tock tock tock

with sound, REGINA only

This REXX version  only   executes when using the Regina REXX interpreter.

/*REXX program simulates a  metronome  (with sound).    Regina REXX only.               */parseargbpmbpbdurtockftockdtickftickd./*obtain optional arguments from the CL*/ifbpm==''|bpm==","thenbpm=72/*the number of  beats  per minute.    */ifbpb==''|bpb==","thenbpb=4/* "    "     "    "     "   bar.      */ifdur==''|dur==","thendur=5/*duration  of the  run         in secs*/iftockf==''|tockf==","thentockf=400/*frequency  "  "   tock  sound  " HZ. */iftockd==''|tockd==","thentockd=20/*duration   "  "     "     "    " msec*/iftickf==''|tickf==","thentickf=600/*frequency  "  "   tick    "    " HZ. */iftickd==''|tickd==","thentickd=10/*duration   "  "     "     "    " msec*/calltime'Reset'/*reset the REXX elapsed timer.        */bt=1/bpb/*calculate a   tock─time   interval.  */dountilet>=dur;et=time('Elasped')/*process  tick-tocks  for the duration*/callbeeptockf,tockd/*sound a beep for the  "TOCK".        */es=et+1/*bump the elapsed time  "limiter".    */$t=et+btdountile>=es;e=time('Elapsed')ife<$ttheniterate/*time for tock?     */callbeeptickf,tickd/*sound a  "tick".   */$t=$t+bt/*bump the TOCK time.*/end/*until e≥es*/end/*until et≥dur*//*stick a fork in it,  we're all done. */

with sound, PC/REXX only

/*REXX program simulates a  metronome  (with sound).      PC/REXX or Personal REXX only.*/parseargbpmbpbdurtockftockdtickftickd./*obtain optional arguments from the CL*/ifbpm==''|bpm==","thenbpm=72/*the number of  beats  per minute.    */ifbpb==''|bpb==","thenbpb=4/* "    "     "    "     "   bar.      */ifdur==''|dur==","thendur=5/*duration  of the  run         in secs*/iftockf==''|tockf==","thentockf=400/*frequency  "  "   tock  sound  " HZ. */iftockd==''|tockd==","thentockd=.02/*duration   "  "     "     "    " sec.*/iftickf==''|tickf==","thentickf=600/*frequency  "  "   tick    "    " HZ. */iftickd==''|tickd==","thentickd=.01/*duration   "  "     "     "    " sec.*/calltime'Reset'/*reset the REXX elapsed timer.        */bt=1/bpb/*calculate a   tock─time   interval.  */dountilet>=dur;et=time('Elasped')/*process  tick-tocks  for the duration*/callsoundtockf,tockd/*sound a beep for the  "TOCK".        */es=et+1/*bump the elapsed time  "limiter".    */$t=et+btdountile>=es;e=time('Elapsed')ife<$ttheniterate/*time for tock?     */callsoundtickf,tickd/*sound a  tick.     */$t=$t+bt/*bump the TOCK time.*/end/*until e≥es*/end/*until et≥dur*//*stick a fork in it,  we're all done. */

RPL

« .02 0 → pattern duration beat  « -56 CF@ sound ON    60 SWAP / duration - →NUMDO "Beat!" 1 DISP       440 'beat' INCR pattern MOD 1 2 IFTE * duration BEEP       CLLCD DUP WAITUNTIL KEYEND DROP2» » 'METRO' STO@ ( bpm pattern → )

Ruby

This code rings the audible bell on every beat and write "And n" to stdout where n is the bar number that was just finished

#!/usr/bin/rubybpm=Integer(ARGV[0])rescue60# sets BPM by the first command line argument, set to 60 if none providedmsr=Integer(ARGV[1])rescue4# sets number of beats in a measure by the second command line argument, set to 4 if none providedi=0loopdo(msr-1).timesdoputs"\a"sleep(60.0/bpm)endputs"\aAND#{i+=1}"sleep(60.0/bpm)end

Scala

defmetronome(bpm:Int,bpb:Int,maxBeats:Int=Int.MaxValue){valdelay=60000L/bpmvarbeats=0do{Thread.sleep(delay)if(beats%bpb==0)print("\nTICK ")elseprint("tick ")beats+=1}while(beats<maxBeats)println()}metronome(120,4,20)// limit to 20
Output:

See it running in your browser byScastie (JVM).

Sidef

funcmetronome(beats_per_minute=72,beats_per_bar=4){varcounter=0varduration=60/beats_per_minutevarbase_time=Time.micro+durationSTDOUT.autoflush(true)fornext_timein(base_time..Inf`by`duration){if(counter++%% beats_per_bar){print"\nTICK"}else{print" tick"}Sys.sleep(next_time-Time.micro)}}saymetronome(ARGV.map{Num(_)}...)
Output:
% sidef metronome.sf 60 6TICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick tick tickTICK tick tick tick^C

Tcl

This code only rings the bell on the high beat, which occurs at the start of the bar.

packagerequireTcl8.5lassign$argvbpmbpbif{$argc<2}{setbpb4}if{$argc<1}{setbpm60}fconfigurestdout-bufferingnonesetintervalMS[expr{round(60000.0/$bpm)}]setctr0procbeat{}{globalintervalMSctrbpbafter$intervalMSbeat;# Reschedule first, to encourage minimal driftif{[incrctr]==1}{puts-nonewline"\r\a[string repeat { } [expr {$bpb+4}]]\rTICK"}else{puts-nonewline"\rtick[string repeat . [expr {$ctr-1}]]"}if{$ctr>=$bpb}{setctr0}}# Run the metronome until the user uses Ctrl+C...beatvwaitforever

It might be executed like this:

tclsh8.5metronome.tcl904

Wren

Translation of:Kotlin

Modified to ring the bell on each beat.

import"timer"forTimerimport"io"forStdoutvarmetronome=Fn.new{|bpm,bpb,maxBeats|vardelay=(60000/bpm).floorvarbeats=0while(true){Timer.sleep(delay)System.write((beats%bpb==0)?"\n\aTICK ":"\atick ")Stdout.flush()beats=beats+1if(beats==maxBeats)break}System.print()}metronome.call(120,4,20)// limit to 20 beats
Output:
TICK tick tick tick TICK tick tick tick TICK tick tick tick TICK tick tick tick TICK tick tick tick

XPL0

int BPM, Odd, Time0, Delay;[Text(0, "Beats per minute: ");BPM:= IntIn(0);Odd:= true;Delay:= 60_000_000/BPM;         \microseconds per beatrepeat  Time0:= GetTime;        repeat until GetTime-Time0 >= Delay;        Text(0, if Odd then "tick" else " TOCK^m^j");        Odd:= not Odd;until KeyHit;]
Output:
Beats per minute: 120tick TOCKtick TOCKtick TOCKtick TOCKtick
Retrieved from "https://rosettacode.org/wiki/Metronome?oldid=390913"
Categories:
Hidden category:
Cookies help us deliver our services. By using our services, you agree to our use of cookies.

[8]ページ先頭

©2009-2026 Movatter.jp