Thereisnosortofwayformsoffsamplessentthecommitteeprotocolinstead, Midiis a sequenceoftimingeventsthatcouldbesuppliedtomusicalinstrumentsandequipmentasandwhennecessary.
I thinktheoriginalcreatorsoffthemiddIespecificationswereexceptionallycleverandveryforwardthinking, andas a result, themiddIeformerhasreallystoodthetestoftime, somuchsothatitcouldbemodifiedwitheasetoaccommodatenewdevelopmentsintheindustry.
Andithasalsobecomeubiquitousacrosstheindustry.
Itfullysatisfiestheneedsoff, makingsurethatmusicalinstrumentsandequipmentcantalktoeachotherin a sensibleway.
Now, ifyou'renotinterestedinmusic, that's veryknowthatMidicanalsobeusedas a generalpurposetimingprotocol.
Um, maybe I'lltalkaboutthat a littlebitlateron.
Butfrom a programmingperspective, I thinkthatthemiddIefinalprotocolisquiteinteresting, and I wantedtoexploreitin a video.
Keepingthingssynchronizedandquicktakes a priority.
Nowthere's onewasbeing a programmingthat I'm also a bitofanaudionerd, and I havequite a sophisticatedsetupfordabblingaboutwithhomeaudio.
I don't considermyself a skilledmusicianbyanymeans, but I liketothinkthat I am, and I tryandplaymybestWheneveryoustartworkingwithaudio.
OneoftheprimaryconcernsyouhaveisLeighton.
See, inmyexperience, when I'm clumsilyhammeringmykeyboardwithmyfists, I starttonoticethat I canbecomeoutofsyncifthere's a latentseegreaterthan 30 milliseconds, andthatdoesn't seemlike a lot.
WegotthingslikeUSBtwoUSBthreenow, butatthesametime, we'redemanding a lotmorefromour C P usedtoproducemoreinterestingeffectsandmorerealisticsounds.
Theonethingwehavegoingforisanaudioisthatwe'restilltalkingaboutMillerSecondshitinto a computer.
A millisecondisaneternity.
Anyway.
I'm gettingsidetracked.
Let's getbacktothemiddIevideo.
There's severallayersto a MIDIsequence.
Atthemostbottomlayeristheconceptof a channelwhereeachMIDIeventisassociatingwithissuinginformationto a certainchannelonthatchanneltypicallyreflects a specificinstrumentonit's usuallydowntoyourstudio.
Inthisnewtrack, someoftheseeventsmaybemappedontothesamechannelsthatwealreadyhave a peer.
Butothereventscanbemappedtoowell, newchannels, sowecanquicklybuildupquite a largelibraryofvirtualinstrumentsbeforewegoon.
DissecttheMIDIfileformatforourselves.
I thoughtitwouldbeusefultoactuallylookatt a MIDIfileand I foundthispieceofsoftwareit's freetodownload, buttheyencourage a donationsimplycalledMIDIEditor.
Theysound a littlebitflatandoldfashionedtypicalmiddIesounds.
Ifyou'refamiliarwiththose, andthat's becauseonwindowshegetthisMicrosoftGSwavetable, sincewhichis a basicselectionoffvirtualinstruments.
Infact, I canchangethechanneltosomethingelse.
Solet's emulate a pianosoundinstead.
OneofthenicethingsabouteventsintimeandpitchesistheycanbevisualizedAnatodeGrafflikethis, andso I thinkthat's whatwe'llaimfor.
Firstwilltryreadingin a midifileonvisualizingit, and I justlovewatchingthecomplexityofthingslikethis.
Whenitallcomestogether, itproducessomethinground, likeit a lot.
Aswithanythingthatisconsidered a standardsomewhere, thereexistsexspecificationsforthatstandard, andthisisthemiddIespecificationsofficialwebsite, anditcontainslotsofinformationabouthowwecaninterpretwhat's goingoninside a filefiles a littlebitdifferenttojustregularmidistreamsbetweeninstrumentsastheycontainadditionalinformationthattheinstrumentsimplydon't careabout.
Fortunately, stringsinthefilearegoingtobestoredsequentiallyonwillknowthelengthofthestringinadvance, so I'llcreate a littleLambdafunction.
ReadString, whichtakesinthatlengthonallowsaccesstotheinputfileStreamonjustsuccessivelyreadsthecharactersondependsthemto a standardstringobject.
NowwegettothesecondcuriositywithMIDIfiles.
MostMIDIfileswillnevercontaininformationthatrequiresmorethansevenbitstostore, andthatmightseemlikeanunusualdesignchoiceforplatforms, whichroutinelyworkwith a bits.
Butalso 127 intermsofmusicalequipmentisquite a lotofdifferentthings.
Forexample, ifyouhad a keyboardwith 100 and 27 keysonit, that's goingtobequitesomekeyboard.
Alternatively, ifyouwantsomesortofexpressionistictoolincludedinyourminievent, while 127 levelsofdifferentiationwithinthatexpressionisquitefiniteonhighresolution.
Andthattellsuswe'vegotourcompletewordnow 7 14 and 21 bitseemlikeunusualnumbers, so I'm justgoingtostuffallofthoseinto a 32 bitword.
Now, thismayseemlike a veryrudimentarycompression, butitworksverywell.
Whenwe'vegotlowvaluenumbers.
Weonlyneedtotransmit a fewernumberbites.
Greatfirstsystemweretransmitting.
Thingsincreases.
LeightonSee, I'llstorm I accumulatedvalueinthisvariableendvalue, I'm goingtoneed a littlehelpofvariable, whichrepresentsonebite.
Wheninstructedtoread a value, Thefirstthing I'm goingtodoisreadonebitefromthestream.
Nowthatbitecouldbecompletelysufficienttogiveusthefinalvalue, so I'llreadthatintotheendvaluevariable.
But I needtocheckthemostsignificantbitofthisbitebecauseofitsset.
Then I needtokeepreading, so I willtakethebottomsevenbitsofthebitethat I'vejustreadandthenproceedtocontinuouslyreadbitesuntil I readonewherethemostsignificantbitisn't set.
ThemiddIeheaderis a fixedsites, soassoonaswefinishreadingpreciselythatamountofinformationwereintoreadingthedataitselfon, we'llstartbyreadingthetrackdata.
I knowhowmanytracksisgoingtobe.
That's mytrackchunks.
So I'm goingtogotoeloop, whichgoesthroughallofthetracksandjustforconvenience.
I'm goingtooutputtotheconsolethatthisis a newtrackWedidn't know.
It's just a textstringthatidentifies I am a Miditrackandthenextvalueishowmanybitesarecontainedwithinthattrack.
Now, ifyoupassyourMIDIfileproperly, you'renotgoingtoneedthatvalueeither, becausefromnowon, everythingthatwereadisgoingtobe a MIDIevent.
Onallmediaeventsaredeterministicinsizeandcontent.
I guessyoucouldusethetracklengthtocheckforcorruptioninyourMIDIfile, butitalsoallowsyoutocompletelyskippedreading a track.
Ifyouwantedtojustskipthenextentrappedlengthbitesyoucouldgettothenexttrack, however, werenotinterestedindoingthat.
ThereisoneparticularMIDIevent, whichsignifiestheendofthetrack, so I'llcreate a Booleanflagjusttokeeptrackofwhetherthat's happened.
Andthen I willsitin a loop, makingsurethatwedon't prematurelyreachtheendofthefilebutalsokeepinganeyeoutforthateventtooccurwherenowwe'regoingtoreadinandprocessthemiddiemessages.
I'llstartbyassumingthatallMIDIeventscontain a timeDeltavalueunderstatusvalue.
Thestatusvaluewilltelluswhattypeofeventitis, you'llnotice, however, there's a littleAsterixnexttothestate, despitebecausewe'llseelateronthatnotallMIDIeventsdoinclude a statusbite.
Thesecondbiteindicateshowhardthenotewaspressed, sothestatusbitetoldusthatitwas a voicenoteoneventonthespecifications, saysthenexttwobitesrepresenttheideaandthevelocity.
Well, see, thisis a commonpatternthroughout a lotoftheseevents.
Sothevoicenoteoffthat's thekeybeingreleased.
Wereadthesametwothingsnow, eventhoughforthisvideo I don't careaboutwhattheseotherpacketsactuallydo, I doneedtoreadtherightamountofinformationfromhimbecausewe'rereadingthemfrom a stream.
Astor's readingthechannelpressurepitchBenis a littlebitdifferentagain.
Itdoesrequirestoreadtwobites, butthistimetheymeansomethingdifferentonWe'llcomebacktoSystemexclusivein a littlebitshouldalsojustaddinhereandelseunrecognizedstatusbitesjustincasesomethinghashappened.
Well, myintentionistorendertheMIDIfileonthescreenin a waytoosimilartowhatthemiddIeeditor I showedearlierisdoingso.
I don't reallycaretoomuchaboutusingthisassomesortofmidiprocess, sir, but I docareabouttranslatingtheevents I'm readingfromthefileintoinformationthat I canuseinmyapplication.
I knowpotentially, I'm goingtohavemultipletracks.
So I create a structurecalledMidiTrack, which I'm goingtohave a stringforthename a stringfortheinstrument.
SoaswenowreadthroughthefileallofthedifferentMIDIevents, I'm goingtoanthemtothismiddIetrack, and I'llstoreallofthesetracksinmyMIDIfileclassin a vectorofMiditracksatthestartofourloop.
Weknowwe'vegot a newtrack, so I'm goingtopush a blankMIDItrackintothatvector.
Let's takethevoicenotesaftereventisanexample.
I'vereadintherelevantinformation.
I'm nowgoingtoconstruct a MIDIeventusingthatinformationinthiscase.
Andwe'vereadinthelengthofthemessagespecificationssaysthatthisisgoingtobe a stringofthatparticularlength, sowe'rejustgoingtoreadinthatstringwithoutreadstringLambdafunction.
Iftheeventis a noteon, I'm goingtoaddoneofmymanynotestomylistofnotesbeingprocessed.
Theproblemis, I don't knowwhatthedurationofthisnoticeuntil I findthecorrespondingMIDInoteoffeventforthatparticularnoteon, I'm reallysortofhackingthistogether.
I'm surethere's farmoreeffectiveways.
Iftheeventhappenstobe a MIDInoteoff, I'm goingtosearchmylistofcurrentlyprocessednotestoseeifit's thesamekey.
Andifthatsearchyieldssomethingthatmeansthisis a noteoffeventfor a keythat's alreadybeingprocessed.
I nowknowitsduration, andso I canworkoutitsdurationbylookingatthecurrentwalltime, minusthenotethatwefoundstarttime, andsincethisnoteisnolongerbeingprocessed, itgraduatestobeingputintomytracks.
Vectorofnotesand I canerasethenotebeingprocessedfromthelistofnotesbeingprocessed.
Max, NoteandMinnoteandupsetthemto 64 potentially, for a mediaevent.
Thereare 100 and 27 notes.
Thisisveryraretoseesomethinggoingfromzeroto 127 andif I wastovisualizethis, I wouldhave a lotofblankscreenwithjust a smalllineofthingshappeninginthemiddleofit.
So I'm goingtorecordwhatthemaximumnotepressedwaasandwhattheminimumnotepressedwaason.
Thatway I canscaletheheightofmytrackvisualization, and I'llmake a checkforthathere.
Andthiswillallowmethatwhen I'm visualizingthetrack, I don't needtodrawlotsofblank, emptyspaceaboveandbelowwhere, really, alltheactionishappeningintheMIDIfile.
Let's quicklyconstructthevisualization.
First, I'm goingtoclearthewholescreentoeblack, and I'lladdtotheclass a 32 bitunsignedvariableandtrackoffset.
What I'm goingtodoisusedtrackoffsettomoveusforwardandbackwardsthroughtime.
I'm onlyinterestedinnoteson a Notoughbecauseweknowtheminimumandmaximumnoteof a particulartrack.
I knowwhattherangeisforthattrack, andso I'm goingtodraw a greatrectangleacrossthescreen, usingthePhilRechtfunction, thesameheightastherangeofthatparticulartrack.
I'm alsogoingtodrawthenameofthattracktwo.
I'vejustintroducedthisendoffsetWhyvariable?
We'regoingtoneedtochangethisaswedrawthetracksbecauseeachtrackisgoingtohave a differentheight, andwe'regoingtohaveseveraltracksgoingdownthescreen.
Nowit's timetodrawthenoteson.
I'm notgoingtooptimizethisintheslightest.
I'm justgoingtodrawthemall, evenifwecan't seethemajorityoffthem, eachnoteisgoingtobedrawnas a filledrectangle.
The X coordinateforthestartingpointofthatrectanglewealreadyknowbecausewe'veextracteditfromtheeventsequenceonGotooffsetitbyourtrackoffsetvalue.
Anddon't forgetthatonecolumnofpixelsrepresents 50 ticks, so I needtodividethisnumberbythe 50 toeffectivelyscaleitintheexdirection.
The Y positionofthenoteisdeterminedbywherewe'restartingtodrawthetrackonthescreenandthekeyvalueoffthenotethataffectsitsheight.