基于WebAudio API来创建合成音乐效果

看演示效果
废话不多说,直接上代码

整体思路

  • 第一步:创建一个蜂鸣器,就是喇叭,能响就行
  • 第二步:根据音调的不同,创建所需的音符
  • 第三步:设置高低音
  • 第四步:添加按键监听事件,控制播放
  • 设置按键

第一步:创建一个蜂鸣器,就是喇叭,能响就行

var BEEP = {VERSION: 3,//  Chrome, Opera, and Firefox already provide AudioContext()//  but Safari instead provides webkitAudioContext().//  Let’s just standardize this for our own sanity right here:AudioContext: window.AudioContext ? window.AudioContext : window.webkitAudioContext,//  We’ll keep track of Voices, Triggers, and Instruments// (but not Notes because we just throw those away left and right)//  so we can access and/or destroy them later even if unnamed.voices:      [],triggers:    [],instruments: [],//  Once the “DOM Content Loaded” event fires we’ll come back//  and set this to either an existing DOM Element with a #beep ID//  or create a new element and append it to the Body.domContainer: null,//  Destroy everything. EVERYTHING. DO IT. DO IT NOW.destroy: function(){while( this.voices.length ){this.voices.pop().destroy()}while( this.triggers.length ){this.triggers.pop().destroy()}while( this.instruments.length ){this.instruments.pop().destroy()}},//  Create a new Instrument from the code//  currently in the code editor.eval: function(){var code = document.getElementById( 'editor' ).valuetry {eval( code )   }catch( e ){console.log( 'OMFG', e )}},//  Right now just runs BEEP.eval() but in the near future//  we might have some more tricks up our sleeve...boot: function(){this.eval()},//  We need to tear everything down.//  Then build it right back up.reset: function(){this.destroy()this.boot()}
}//  We might as well create an instance of AudioContext
//  that we can re-use over and over if necessary.
//  Why? Because the number of AudioContext instance is
//  limited by hardware. For example, my personal laptop
//  can only handle 6 of them at once!
BEEP.audioContext = new BEEP.AudioContext()//  Once our DOM Content is ready for action
//  it’s time to GIVE IT ACTION. W000000000000T !
document.addEventListener( 'DOMContentLoaded', function(){BEEP.domContainer = document.getElementById( 'beep' )BEEP.boot()
})

第二步:根据音调的不同,创建所需的音符


BEEP.Note = function( params ){var that = thisif( typeof params === 'number' ) this.hertz = paramselse if( typeof params === 'object' && params.hertz !== undefined ){Object.keys( params ).forEach( function( key ){that[ key ] = params[ key ]})}else return BEEP.Note.EDO12( params )
}//  Common Western music has 12 notes per octave,
//  lettered A through G with modifier symbols for sharps and flats.
//  Let’s build a validator for Western music:
BEEP.Note.validateWestern = function( params ){var NAMES   = [ 'A♭', 'A♮', 'B♭', 'B♮', 'C♮', 'C♯', 'D♮', 'E♭', 'E♮', 'F♮', 'F♯', 'G♮' ],LETTERS = 'ABCDEFG',SHARPS  = 'CF',FLATS   = 'EAB',tempif( typeof params === 'undefined' ) params = {}else if( typeof params === 'string' ){temp = paramsparams = {}temp.split( '' ).forEach( function( p, i ){if( +p + '' !== 'NaN' ) params.octaveIndex = +pelse if( '♭♮♯#'.indexOf( p ) !== -1 ){params.modifier = p}else if(( LETTERS + 'H' ).indexOf( p.toUpperCase() ) !== -1 ){if( p.toUpperCase() === 'H' ) params.letter = 'B'else if( p === 'b' && i > 0 ) params.modifier = '♭'else params.letter = p.toUpperCase()}})}//  What octave is this?if( params.octaveIndex === undefined || params.octaveIndex === ''|| +params.octaveIndex +'' === 'NaN' ) params.octaveIndex = 4params.octaveIndex = +params.octaveIndexif( params.octaveIndex < 0 ) params.octaveIndex = 0else if( params.octaveIndex > 7 ) params.octaveIndex = 7//  What’s this Note’s name?if( params.letter === undefined ) params.letter = 'A'params.letterIndex = LETTERS.indexOf( params.letter )if( params.modifier === undefined ) params.modifier = '♮'if( params.A === undefined ) params.A = 440.00//  Force the correct accidental symbols.if( params.modifier === 'b' ) params.modifier = '♭'if( params.modifier === '#' ) params.modifier = '♯'//  Handy function for redefining the letter//  when the letterIndex may have shifted.function setLetterByLetterIndex( params ){if( params.letterIndex < 0 ){params.letterIndex += LETTERS.lengthparams.octaveIndex --}if( params.letterIndex >= LETTERS.length ){params.letterIndex -= LETTERS.length//  Next line commented out but left in as a reminder//  that it would cause G♯ conversion to A♭//  to jump up an entire octave for no good reason!//params.octaveIndex ++}params.letter = LETTERS.substr( params.letterIndex, 1 )return params}//  Force the correct sharp / flat categorization.//  Why does the Equal Temperament scale consider certain letters flat or sharp//  when they are mathematically equal?!//  Has to do with the delta between Equal Temperament and the Just Scale.//  Where Equal Temperament comes in higher than Just we call it sharp,//  and where it comes in lower than Just we call it flat://  http://www.phy.mtu.edu/~suits/scales.htmlif( params.modifier === '♭' && FLATS.indexOf( params.letter ) === -1 ){params.letterIndex = LETTERS.indexOf( params.letter ) - 1params = setLetterByLetterIndex( params )if( SHARPS.indexOf( params.letter ) > -1 ) params.modifier = '♯'else params.modifier = '♮'}else if( params.modifier === '♯' && SHARPS.indexOf( params.letter ) === -1 ){params.letterIndex = LETTERS.indexOf( params.letter ) + 1params = setLetterByLetterIndex( params )if( FLATS.indexOf( params.letter ) > -1 ) params.modifier = '♭'else params.modifier = '♮'}//  Now that we’re certain the modifier is correct//  we can set convenience booleans.if( params.modifier === '♯' ) params.isSharp = trueelse if( params.modifier === '♭' ) params.isFlat = trueelse params.isNatural = true//  A final cleanse. Should test if this is still necessary...params = setLetterByLetterIndex( params )//  Penultimate bits...  params.name = params.letter + params.modifierparams.nameSimple = params.letterif( params.modifier !== '♮' ) params.nameSimple += params.modifierparams.nameIndex = NAMES.indexOf( params.name )params.pianoKeyIndex = params.octaveIndex * 12 + params.nameIndexif( params.nameIndex > 3 ) params.pianoKeyIndex -= 12//  What tuning method are we supposed to use? if( params.tuning === undefined ) params.tuning = 'EDO12'//  We now have the majority of the Note ready for use.//  Everything except for ... the FREQUENCY of the Note!//  That will be decided based on the tuning method.return params
}//  Does exactly what it says on the tin, man.
BEEP.Note.EDO12 = function( params ){params = BEEP.Note.validateWestern( params )params.hertz = params.A * Math.pow( Math.pow( 2, 1 / 12 ), params.pianoKeyIndex - 49 )params.tuning = 'EDO12'return new BEEP.Note( params )
}//  The most mathematically beautiful tuning,
//  makes for sonically gorgeous experiences
//  ... Until you change keys!
BEEP.Note.JustIntonation = function( params, key ){var that = this,relationshipIndexparams = BEEP.Note.validateWestern( params )params.tuning = 'JustIntonation'params.key = new BEEP.Note.EDO12( key )//  This is Ptolemy’s “Intense Diatonic Scale” which is based on //  Pythagorean tuning. It is but one example of Just Intonation.relationshipIndex = ( params.nameIndex - params.key.nameIndex ) % 12if( relationshipIndex < 0 ) relationshipIndex += 12params.hertz = [params.key.hertz,          //  Do  UNISONparams.key.hertz * 16 / 15,//      minor     2ndparams.key.hertz *  9 /  8,//  Re  MAJOR     2ndparams.key.hertz *  6 /  5,//      minor     3rdparams.key.hertz *  5 /  4,//  Mi  MAJOR     3rdparams.key.hertz *  4 /  3,//  Fa  PERFECT   4thparams.key.hertz * 45 / 32,//      augmented 4thparams.key.hertz *  3 /  2,//  So  PERFECT   5thparams.key.hertz *  8 /  5,//      minor     6thparams.key.hertz *  5 /  3,//  La  MAJOR     6thparams.key.hertz * 16 /  9,//      minor     7th (HD, baby!)params.key.hertz * 15 /  8,//  Ti  MAJOR     7thparams.key.hertz *  2      //  Do  OCTAVE][ relationshipIndex ]//  If the key’s octave and our desired note’s octave were equal//  then we’d be done. Otherwise we’ve got to bump up or down our //  note by whole octaves.params.hertz = params.hertz * Math.pow( 2, params.octaveIndex - params.key.octaveIndex )return new BEEP.Note( params )
}

第三步:设置高低音

BEEP.Voice = function( a, b ){//  Remember the ? will be validated by Note()//  so it could be an Object, String, Number, etc.////      ( AudioContext, Note )//      ( AudioContext, ?    )//      ( GainNode,     Note )//      ( GainNode,     ?    )if( a instanceof BEEP.AudioContext || a instanceof GainNode ){if( a instanceof BEEP.AudioContext ){this.audioContext = athis.destination  = a.destination}else if( a instanceof GainNode ){this.audioContext = a.audioContextthis.destination  = a}if( b instanceof BEEP.Note ) this.note = belse this.note = new BEEP.Note( b )//  Still ok if b === undefined.}//  Again, the ? will be validated by Note()//  so it could be an Object, String, Number, etc.////      ( Note               )//      ( Note, AudioContext )//      ( Note, GainNode     )//      ( ?                  )//      ( ?,    AudioContext )//      ( ?,    GainNode     )else {if( a instanceof BEEP.Note ) this.note = aelse this.note = new BEEP.Note( a )//  Still ok if a === undefined.if(  b instanceof BEEP.AudioContext ){this.audioContext = bthis.destination  = b.destination}else if( b instanceof GainNode ){this.audioContext = b.audioContextthis.destination  = b}else {this.audioContext = BEEP.audioContextthis.destination  = this.audioContext.destination}}//  Create a Gain Node//  for turning this voice up and down.this.gainNode = this.audioContext.createGain()this.gainNode.gain.value = 0this.gainNode.connect( this.destination )this.gainHigh = this.note.gainHigh !== undefined ? this.note.gainHigh : 1//  Create an Oscillator//  for generating the sound.this.oscillator = this.audioContext.createOscillator()this.oscillator.connect( this.gainNode )this.oscillator.type = 'sine'this.oscillator.frequency.value = this.note.hertz//  Right now these do nothing; just here as a stand-in for the future.this.duration = Infinitythis.attack   = 0this.decay    = 0this.sustain  = 0//  Because of “iOS reasons” we cannot begin playing a sound//  until the user has tripped an event.//  So we’ll use this boolean to trip this.oscillator.start(0)//  on the first use of this Voice instance.this.isPlaying = false//  Push a reference of this instance into BEEP’s library//  so we can access and/or destroy it later.BEEP.voices.push( this )
}//  Voices are *always* emitting, so “playing” a Note
//  is really a matter of turning its amplitude up.
BEEP.Voice.prototype.play = function( params ){//  Let’s create that Note.//  The params will specify a frequency assignment method to use//  otherwise Note() will pick a default.if( params !== undefined ) this.note = new BEEP.Note( params )this.oscillator.frequency.value = this.note.hertzthis.gainNode.gain.value = this.gainHigh || params.gainHigh || 1//  Oh, iOS. This “wait to play” shtick is for you.if( this.isPlaying === false ){this.isPlaying = truethis.oscillator.start( 0 )}return this
}BEEP.Voice.prototype.pause = function(){this.gainNode.gain.value = 0return this
}BEEP.Voice.prototype.destroy = function(){if( this.isPlaying ) this.oscillator.stop( 0 )// Stop oscillator after 0 seconds.this.oscillator.disconnect()// Disconnect oscillator so it can be picked up by browser’s garbage collector.return this
}BEEP.Voice.prototype.getGainHigh = function(){return this.gainHigh
}
BEEP.Voice.prototype.setGainHigh = function( normalizedNumber ){this.gainHigh = normalizedNumberreturn this
}
BEEP.Voice.prototype.getOscillatorType = function(){return this.oscillator.type
}
BEEP.Voice.prototype.setOscillatorType = function( string ){this.oscillator.type = stringreturn this
}

第四步:添加按键监听事件,控制播放

BEEP.Trigger = function(){var that = this//  Trigger is rather permissive with its parameters.//  You can send it an Instrument, Note, a replacement function//  for its createVoices() method, or something that might//  possibly be a valid Note if you ran it through Note().//  Don’t shoot your eye out, kiddo.Array.prototype.slice.call( arguments ).forEach( function( arg ){if( arg instanceof BEEP.Instrument ) that.instrument = argelse if( arg instanceof Function ) that.createVoices = argelse if( arg instanceof BEEP.Note ) that.note = argelse that.note = new BEEP.Note( arg )})//  Might be a grand idea to have a unique ID in case anyone should//  need that down the road.this.id = Date.now() +'-'+ Math.round( Math.random() * 10000000000 )//  If we already have an Instrument then we ought plug into//  its existing Audio Context. Otherwise we’ll aim straight for//  the “global” BEEP one.if( this.instrument ) this.audioContext = this.instrument.audioContextelse this.audioContext = BEEP.audioContext//  What if we didn’t receive anything useful as a Note?//  We’ll just run with defaults.if( this.note === undefined ) this.note = new BEEP.Note()//  Now that we have an Audio Context we should add a buffer of Voices.//  Also good to know if our Trigger is engaged or not!this.engaged = falsethis.voices  = []this.createVoices()//  This container visually houses the note name//  and the visible trigger below it.this.domContainer = document.createElement( 'div' )this.domContainer.classList.add( 'trigger-container' )if( this.note.isSharp || this.note.isFlat ) this.domContainer.classList.add( 'unnatural' )else this.domContainer.classList.add( 'natural' )//  This is pretty useful for CSS tricks tied to note names (sans octave)//  like say... A RAINBOW ROLL !this.domContainer.classList.add( 'name-index-'+ this.note.nameIndex )//  Who knows, this might be useful in the future.this.domContainer.setAttribute( 'id', 'trigger-'+ this.id )    //  Every note has a name.//  This note’s name is Robert Paulson. //  HIS NAME IS ROBERT PAULSON.this.domNoteName = document.createElement( 'div' )this.domNoteName.classList.add( 'note-name' )this.domNoteName.innerHTML = '<strong>'+ this.note.nameSimple +'</strong>'+ this.note.octaveIndexthis.domContainer.appendChild( this.domNoteName )//  This is the actual visible trigger,//  the primary visual element of a keyboard interface.//  And the target of our mouse / touch events.this.domTrigger = document.createElement( 'div' )this.domTrigger.classList.add( 'trigger' )this.domContainer.appendChild( this.domTrigger )//  This will house a list of all keyboard inputs//  that trigger this, uh ... Trigger.this.domCharsList = document.createElement( 'div' )this.domCharsList.classList.add( 'chars-list' )this.domTrigger.appendChild( this.domCharsList )//  We’re either attaching all this DOM baggage to//  a proper Instrument DOM elment //  or straight to the Document Body element!if( this.instrument && this.instrument.domTriggers ) this.instrument.domTriggers.appendChild( this.domContainer )else document.body.appendChild( this.domContainer )//  Add some mouse and touch events.this.eventListeners = []this.domTrigger.addEventListener( 'mouseenter', function(){ that.engage( 'mouseenter' )})this.domTrigger.addEventListener( 'mouseleave', function(){ that.disengage( 'mouseenter' )})this.domTrigger.addEventListener( 'touchstart', function( event ){that.engage( 'touched' )event.preventDefault()})this.domTrigger.addEventListener( 'touchend', function( event ){that.disengage( 'touched' )event.preventDefault()})//  Push a reference of this instance into BEEP’s library//  so we can access and/or destroy it later.BEEP.triggers.push( this )
}BEEP.Trigger.prototype.addEventListener = function( type, action ){this.eventListeners.push({type: type,action: action})window.addEventListener( type, action )
}
BEEP.Trigger.prototype.removeEventListener = function( type, action ){window.removeEventListener( type, action )//@@  can we remove this shit from this.eventListeners then???
}//  You can add as many or as few trigger characters you like.
//  Why would you want to add more?
//  Try out the default synthesizer and see what happens when
//  you walk the keys up an octave.
//  Is the higher C where you expected it to be?
//  @@ TODO:
//  Does it make sense to add these listeners to instrument.domContainer instead of window?
BEEP.Trigger.prototype.addTriggerChar = function( trigger ){var that = this,triggerChar,triggerCharCodeif( typeof trigger === 'string' ){triggerChar = trigger.toUpperCase()triggerCharCode = triggerChar.charCodeAt( 0 )if( triggerChar === '<' ) triggerCharCode = 188// Ad hoc conversion of ASCII to KeyCode.}else if( typeof trigger === 'number' ){triggerCharCode = triggertriggerChar = String.fromCharCode( triggerCharCode )if( triggerCharCode === 188 ) triggerChar = '<'// Ad hoc conversion of KeyCode to ASCII.}this.addEventListener('keydown', function( event ){var keyCode = event.which || event.keyCodeif( keyCode === triggerCharCode && !event.metaKey && !event.ctrlKey ) that.engage( 'keydown-'+ triggerCharCode )})this.addEventListener('keyup', function( event ){var keyCode = event.which || event.keyCodeif( keyCode === triggerCharCode && !event.metaKey && !event.ctrlKey ) that.disengage( 'keydown-'+ triggerCharCode )})this.domCharsList.innerHTML += '<br>'+ triggerCharreturn this
}//  This is the default createVoices() function. You can easily override this
//  by sending your own Function to the Trigger constructor, or even sending
//  your own Function to Instrument, which will in turn pass it on to each
//  Trigger instance that it builds.
// “Down here, it’s our time. It’s our time down here.
//  That’s all over the second we ride up Troy’s bucket.”
BEEP.Trigger.prototype.createVoices = function(){//  Let’s call this our “Foundation Voice”//  because it will sing the intended Note.this.voices.push( new BEEP.Voice( this.note, this.audioContext ).setOscillatorType( 'square' ).setGainHigh( 0.2 ))//  This Voice will sing 1 octave below the Foundation Voice.this.voices.push( new BEEP.Voice( this.note.hertz / 2, this.audioContext ).setOscillatorType( 'sine' ).setGainHigh( 0.3 ))
}//  All-stop. Kill all the voices (in your head).
BEEP.Trigger.prototype.destroyVoices = function(){var i = this.voices.lengthwhile( i -- ){if( this.voices[ i ] !== undefined && typeof this.voices[ i ].pause === 'function' ) this.voices[ i ].pause()delete this.voices[ i ]}this.voices  = []this.engaged = falsereturn this
}BEEP.Trigger.prototype.play = function(){this.voices.forEach( function( voice ){ voice.play() })
}
BEEP.Trigger.prototype.pause = function(){this.voices.forEach( function( voice ){ voice.pause() })
}//  Engage() and disengage() are like wrappers for
//  play() and stop() respectively
//  with safety mechanisms and interface feedback.
BEEP.Trigger.prototype.engage = function( eventType ){if( this.engaged === false ){this.engaged = truethis.eventType = eventTypethis.domContainer.classList.add( 'engaged' )this.play()}return this
}
BEEP.Trigger.prototype.disengage = function( eventType ){if( this.engaged === true && ( this.eventType === eventType || this.eventType === 'code' )){this.engaged = false      this.pause()this.domContainer.classList.remove( 'engaged' )}return this
}//  If you’re replacing your Instrument’s keyboard
//  it might be useful to dispose of its Triggers in
//  a meaningful way.
BEEP.Trigger.prototype.destroy = function(){this.pause()this.eventListeners.forEach( function( event ){window.removeEventListener( event.type, event.action )})    this.eventListeners = []this.domContainer.remove()
}

设置按键

BEEP.Instrument = function(){var that = thisArray.prototype.slice.call( arguments ).forEach( function( arg ){if( arg instanceof window.Element ) that.domContainer = argelse if( typeof arg === 'string'  ) that.domContainer = document.getElementById( arg )else if( arg instanceof Function  ) that.createVoices = arg})//  Let’s hook up to BEEP’s “global” Audio Context.this.context = BEEP.audioContext//  Now that we have an Audio Context we can give our Instrument//  its own volume knob.//  @@  Should add DAT GUI or similiar to control this...this.gainNode = this.context.createGain()this.gainNode.connect( this.context.destination )this.gainNode.gain.value = 0.3//  We may have passed in a DOM Element as a target for this Instrument//  or a String representing a DOM Element’s ID.//  Otherwise we need to build a DOM Element and attach it.if( this.domContainer === undefined ) this.domContainer = document.createElement( 'div' )this.domContainer.classList.add( 'instrument' )if( BEEP.domContainer ) BEEP.domContainer.appendChild( this.domContainer )else document.body.appendChild( this.domContainer )    //  What’s an Instrument without an interface?//  Let’s add storage for our Triggers.this.triggers = {}this.domTriggers = document.createElement( 'div' )this.domTriggers.classList.add( 'triggers' )this.domContainer.appendChild( this.domTriggers )//  And we could use a handy interface button//  for playing the score we’re going to load.var playPauseContainer = document.getElementById( 'play-pause-container' )if( !playPauseContainer ) playPauseContainer = this.domContainerthis.domScorePlayPause = document.createElement( 'div' )this.domScorePlayPause.classList.add( 'score-play-pause' )playPauseContainer.appendChild( this.domScorePlayPause )this.domScorePlayPause.addEventListener( 'click', function(){ that.scoreToggle() })this.domScorePlayPause.addEventListener( 'touchend', function( event ){ that.scoreToggle()event.preventDefault()})//  Might be nice if Spacebar can play / pause a score.window.addEventListener( 'keypress', function( event ){var keyCode = event.which || event.keyCodeif( keyCode === 32 ){that.scoreToggle()event.preventDefault()}//  OMFG this is annoying.//  We cannot reliably detect the ESCAPE key here//  because of this problem in Chrome: //  https://github.com/philc/vimium/issues/499//  Temporarily using SHIFT + ENTER key instead....else if( keyCode === 13 && event.shiftKey && that.scoreIsPlaying === false ){if( Object.keys( that.triggers ).length ) that.unbuild()else that.build()event.preventDefault()}})//  Each Trigger will handle its own touch-start and touch-end//  but touch-move must be handled by the Trigger’s container.this.domContainer.addEventListener( 'touchmove', function( event ){//  What are the bounds {X Y W H} of for each touch-move?//  Does that intersect with bounds for any of our triggers?//  @@  IN THE FUTURE WE’LL ADD EVENT.FORCE :)Array.prototype.slice.call( event.changedTouches ).forEach( function( touch ){Object.keys( that.triggers ).forEach( function( triggerKey ){var trigger = that.triggers[ triggerKey ],rect = trigger.domTrigger.getBoundingClientRect()if( rect.left   < touch.pageX &&touch.pageX < rect.right   &&rect.top    < touch.pageY &&touch.pageY < rect.bottom ){trigger.engage( 'touched' )}else trigger.disengage( 'touched' )})})event.preventDefault()})this.domContainer.addEventListener( 'touchend', function( event ){Object.keys( that.triggers ).forEach( function( triggerKey ){that.triggers[ triggerKey ].disengage( 'touched' )})})//  Maybe this instrument should play some tunes?//  Perhaps makes sense to move all of this into a score.js//  rather than have it be a part of Instrument?this.bpm            = 140//  Beats per minute.this.beatsPerBar    =   4//  Not in use yet.this.oneBeat        = 1/4//  Quarter note.this.beats          =   0//  Current beat... Maybe change name?this.timePrevious   =   0this.scoreCompleted =  []this.scoreRemaining =  []this.scoreIsPlaying = falsethis.build()//  Push a reference of this instance into BEEP’s library//  so we can access and/or destroy it later.BEEP.instruments.push( this )
}
BEEP.Instrument.prototype.destroy = function(){}BEEP.Instrument.prototype.newTrigger = function( note, triggerChars ){var triggerif( note instanceof BEEP.Note === false ) note = new BEEP.Note( note )   //  Here we’re going to assume if we intentionally sent a createVoices()//  function to our Instrument then we’d like all Triggers to use it.//  Otherwise you could quite easily send unique functions for creating//  voices to each individual Trigger, eh?if( this.createVoices !== undefined )trigger = new BEEP.Trigger( this, note, this.createVoices )else trigger = new BEEP.Trigger( this, note )//  What keyboard character or characters should trigger this Trigger?if( triggerChars instanceof Array === false ) triggerChars = [ triggerChars ]triggerChars.forEach( function( triggerChar ){if( triggerChar !== undefined ) trigger.addTriggerChar( triggerChar )})//  We’ll go with this format for ID’s://  Octave # + Note Name (sans any Natural symbols).this.triggers[ note.octaveIndex + note.nameSimple ] = triggerreturn this
}
BEEP.Instrument.prototype.play = function( trigger ){var triggersArray  = Object.keys( this.triggers ),triggersMiddle = Math.floor( triggersArray.length / 2 )//  Middle C on our standard 2 octave build.if( trigger === undefined ) trigger = triggersArray[ triggersMiddle ]if( typeof trigger === 'string' && this.triggers[ trigger ]) trigger = this.triggers[ trigger ]if( trigger instanceof BEEP.Trigger ) trigger.engage( 'code' )return this
}
BEEP.Instrument.prototype.pause = function( trigger ){var that = thisif( typeof trigger === 'string' && this.triggers[ trigger ]) trigger = this.triggers[ trigger ]if( trigger instanceof BEEP.Trigger ) trigger.disengage()if( trigger === undefined ) Object.keys( this.triggers ).forEach( function( trigger ){that.triggers[ trigger ].disengage()//  Kill eveything now regardless of who started it!})return this
}BEEP.Instrument.prototype.buildStandard = function(){this.unbuild().newTrigger( '3C' , 'z' ).newTrigger( '3C♯', 's' ).newTrigger( '3D' , 'x' ).newTrigger( '3E♭', 'd' ).newTrigger( '3E' , 'c' ).newTrigger( '3F' , 'v' ).newTrigger( '3F♯', 'g' ).newTrigger( '3G' , 'b' ).newTrigger( '3A♭', 'h' ).newTrigger( '3A' , 'n' ).newTrigger( '3B♭', 'j' ).newTrigger( '3B' , 'm' ).newTrigger( '4C' , [ 'q', '<' ]).newTrigger( '4C♯', '2' ).newTrigger( '4D' , 'w' ).newTrigger( '4E♭', '3' ).newTrigger( '4E' , 'e' ).newTrigger( '4F' , 'r' ).newTrigger( '4F♯', '5' ).newTrigger( '4G' , 't' ).newTrigger( '4A♭', '6' ).newTrigger( '4A' , 'y' ).newTrigger( '4B♭', '7' ).newTrigger( '4B' , 'u' ).newTrigger( '5C' , 'i' )//  We’ve loaded a default set of Triggers with this.build()//  and now we might as well load a default score//  so it’s dead easy to demonstrate how this works.this.scoreLoadDoReMi()this.scoreLoadFromHash()return this
}
BEEP.Instrument.prototype.buildCloseEncounters = function(){this.unbuild().newTrigger( '4G', '1' ).newTrigger( '4A', '2' ).newTrigger( '4F', '3' ).newTrigger( '3F', '4' ).newTrigger( '4C', '5' )return this
}
BEEP.Instrument.prototype.buildCloseEncountersJust = function(){//@@  EVERYTHING IS ONE OCTAVE LOWER THAN SHOULD BE! ASIDE FROM '4A' !!this.unbuild().newTrigger( new BEEP.Note.JustIntonation( '4G', '4C' ), '1' ).newTrigger( new BEEP.Note.JustIntonation( '4A', '4C' ), '2' ).newTrigger( new BEEP.Note.JustIntonation( '4F', '4C' ), '3' ).newTrigger( new BEEP.Note.JustIntonation( '3F', '4C' ), '4' ).newTrigger( new BEEP.Note.JustIntonation( '4C', '4C' ), '5' )return this
}
BEEP.Instrument.prototype.buildJustVsEDO12 = function(){this.unbuild().newTrigger( '4C', '1' ).newTrigger( '4D', '2' ).newTrigger( '4A', '3' ) .newTrigger( new BEEP.Note.JustIntonation( '4C', '4C' ), '7' ).newTrigger( new BEEP.Note.JustIntonation( '4D', '4C' ), '8' ).newTrigger( new BEEP.Note.JustIntonation( '4A', '4C' ), '9' )return this
}
BEEP.Instrument.prototype.buildC = function(){this.unbuild().newTrigger( '3C', 'z' ).newTrigger( '3C♯' ).newTrigger( '3D', 'x' ).newTrigger( '3E♭' ).newTrigger( '3E', 'c' ).newTrigger( '3F', 'v' ).newTrigger( '3F♯' ).newTrigger( '3G', 'b' ).newTrigger( '3A♭' ).newTrigger( '3A', 'n' ).newTrigger( '3B♭' ).newTrigger( '3B', 'm' ).newTrigger( '4C' , [ 'a', '<' ]).newTrigger( '4C♯' ).newTrigger( '4D', 's' ).newTrigger( '4E♭' ).newTrigger( '4E', 'd' ).newTrigger( '4F', 'f' ).newTrigger( '4F♯' ).newTrigger( '4G', 'g' ).newTrigger( '4A♭' ).newTrigger( '4A', 'h' ).newTrigger( '4B♭' ).newTrigger( '4B', 'j' ).newTrigger( '5C', [ 'k', 'q' ]).newTrigger( '5C♯' ).newTrigger( '5D', 'w' ).newTrigger( '5E♭' ).newTrigger( '5E', 'e' ).newTrigger( '5F', 'r' ).newTrigger( '5F♯' ).newTrigger( '5G', 't' ).newTrigger( '5A♭' ).newTrigger( '5A', 'y' ).newTrigger( '5B♭' ).newTrigger( '5B', 'u' ).newTrigger( '6C', [ 'i', '1' ]).newTrigger( '6C♯' ).newTrigger( '6D', '2' ).newTrigger( '6E♭' ).newTrigger( '6E', '3' ).newTrigger( '6F', '4' ).newTrigger( '6F♯' ).newTrigger( '6G', '5' ).newTrigger( '6A♭' ).newTrigger( '6A', '6' ).newTrigger( '6B♭' ).newTrigger( '6B', '7' ).newTrigger( '7C', '8' ).domContainer.classList.add( 'mini' )return this
}
BEEP.Instrument.prototype.buildCRainbow = function(){this.buildC()this.domContainer.classList.add( 'rainbow' )this.scoreLoadDoReMi()this.scoreLoadFromHash()return this
}
BEEP.Instrument.prototype.buildCJust = function(){this.unbuild().newTrigger( new BEEP.Note.JustIntonation( '3C' , '4C' ), 'z' ).newTrigger( new BEEP.Note.JustIntonation( '3C♯', '4C' )).newTrigger( new BEEP.Note.JustIntonation( '3D' , '4C' ), 'x' ).newTrigger( new BEEP.Note.JustIntonation( '3E♭', '4C' )).newTrigger( new BEEP.Note.JustIntonation( '3E' , '4C' ), 'c' ).newTrigger( new BEEP.Note.JustIntonation( '3F' , '4C' ), 'v' ).newTrigger( new BEEP.Note.JustIntonation( '3F♯', '4C' )).newTrigger( new BEEP.Note.JustIntonation( '3G' , '4C' ), 'b' ).newTrigger( new BEEP.Note.JustIntonation( '3A♭', '4C' )).newTrigger( new BEEP.Note.JustIntonation( '3A' , '4C' ), 'n' ).newTrigger( new BEEP.Note.JustIntonation( '3B♭', '4C' )).newTrigger( new BEEP.Note.JustIntonation( '3B' , '4C' ), 'm' ).newTrigger( new BEEP.Note.JustIntonation( '4C' , '4C' ) , [ 'a', '<' ]).newTrigger( new BEEP.Note.JustIntonation( '4C♯', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '4D' , '4C' ), 's' ).newTrigger( new BEEP.Note.JustIntonation( '4E♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '4E' , '4C' ), 'd' ).newTrigger( new BEEP.Note.JustIntonation( '4F' , '4C' ), 'f' ).newTrigger( new BEEP.Note.JustIntonation( '4F♯', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '4G' , '4C' ), 'g' ).newTrigger( new BEEP.Note.JustIntonation( '4A♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '4A' , '4C' ), 'h' ).newTrigger( new BEEP.Note.JustIntonation( '4B♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '4B' , '4C' ), 'j' ).newTrigger( new BEEP.Note.JustIntonation( '5C' , '4C' ), [ 'k', 'q' ]).newTrigger( new BEEP.Note.JustIntonation( '5C♯', '4C' )).newTrigger( new BEEP.Note.JustIntonation( '5D' , '4C' ), 'w' ).newTrigger( new BEEP.Note.JustIntonation( '5E♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '5E' , '4C' ), 'e' ).newTrigger( new BEEP.Note.JustIntonation( '5F' , '4C' ), 'r' ).newTrigger( new BEEP.Note.JustIntonation( '5F♯', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '5G' , '4C' ), 't' ).newTrigger( new BEEP.Note.JustIntonation( '5A♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '5A' , '4C' ), 'y' ).newTrigger( new BEEP.Note.JustIntonation( '5B♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '5B' , '4C' ), 'u' ).newTrigger( new BEEP.Note.JustIntonation( '6C' , '4C' ), [ 'i', '1' ]).newTrigger( new BEEP.Note.JustIntonation( '6C♯', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '6D' , '4C' ), '2' ).newTrigger( new BEEP.Note.JustIntonation( '6E♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '6E' , '4C' ), '3' ).newTrigger( new BEEP.Note.JustIntonation( '6F' , '4C' ), '4' ).newTrigger( new BEEP.Note.JustIntonation( '6F♯', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '6G' , '4C' ), '5' ).newTrigger( new BEEP.Note.JustIntonation( '6A♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '6A' , '4C' ), '6' ).newTrigger( new BEEP.Note.JustIntonation( '6B♭', '4C' ) ).newTrigger( new BEEP.Note.JustIntonation( '6B' , '4C' ), '7' ).newTrigger( new BEEP.Note.JustIntonation( '7C' , '4C' ), '8' ).domContainer.classList.add( 'mini' )return this
}
BEEP.Instrument.prototype.build = BEEP.Instrument.prototype.buildStandard
BEEP.Instrument.prototype.unbuild = function(){var that = thisObject.keys( this.triggers ).forEach( function( trigger ){that.triggers[ trigger ].destroy()delete that.triggers[ trigger ]})this.domContainer.classList.remove( 'mini' )this.domContainer.classList.remove( 'rainbow' )return this
}BEEP.Instrument.prototype.scoreLoadFromHash = function(){if( document.location.hash !== '' ){var score = document.location.hash.substr( 1 ).split( ',' ).map( function( element ){var valuetry {value = eval( element )}catch( error ){}return value})this.scoreUnload()this.scoreLoad( score )}
}
BEEP.Instrument.prototype.scoreLoad = function( score ){var beat = 0, i, notefor( i = 0; i < score.length; i += 3 ){note = new BEEP.Note( score[ i+1 ])beat += score[ i+0 ] || 0this.scoreRemaining.push([ beat, note.octaveIndex + note.nameSimple, true  ])this.scoreRemaining.push([ beat + score[ i+2 ], note.octaveIndex + note.nameSimple, false ])}this.scoreRemaining.sort( function( a, b ){return a[ 0 ] - b[ 0 ]})return this
}
BEEP.Instrument.prototype.scorePlayLoop = function(){var performant = window.performance && window.performance.now,now = performant ? performance.now() : Date.now(),delta = now - this.timePreviousif( this.timePrevious === 0 ) delta = 1while( this.scoreRemaining.length && this.beats >= this.scoreRemaining[ 0 ][ 0 ] ){if( this.scoreRemaining[ 0 ][ 2 ]) this.play( this.scoreRemaining[ 0 ][ 1 ] )else this.pause( this.scoreRemaining[ 0 ][ 1 ] )this.scoreCompleted.push( this.scoreRemaining.shift() )}this.beats += this.bpm / 60 / delta * this.oneBeatthis.timePrevious = nowif( this.scoreRemaining.length === 0 ){this.scoreIsPlaying = falsethis.scoreRemaining = this.scoreRemaining.concat( this.scoreCompleted )this.scoreCompleted = []this.beats          = 0this.timePrevious   = 0this.domScorePlayPause.classList.remove( 'is-playing' )}if( this.scoreIsPlaying ) requestAnimationFrame( this.scorePlayLoop.bind( this ))return this
}
BEEP.Instrument.prototype.scorePlay = function(){if( Object.keys( this.triggers ).length ){this.scoreIsPlaying = truethis.scorePlayLoop()this.domScorePlayPause.classList.add( 'is-playing' )}return this
}
BEEP.Instrument.prototype.scoreStop = function(){this.scoreIsPlaying = falsethis.pause()this.domScorePlayPause.classList.remove( 'is-playing' )return this
}
BEEP.Instrument.prototype.scoreToggle = function(){if( this.scoreIsPlaying ) this.scoreStop()else this.scorePlay()return this
}
BEEP.Instrument.prototype.scoreUnload = function(){this.scoreStop()this.scoreCompleted = []this.scoreRemaining = []this.beats          = 0this.timePrevious   = 0
}//  http://en.wikipedia.org/wiki/Solf%C3%A8ge
BEEP.Instrument.prototype.scoreLoadDoReMi = function(){varmelody = [36/4, '4C',  6/4,//  Do[e]6/4, '4D',  2/4,//  a2/4, '4E',  5/4,//  deer     6/4, '4C',  2/4,//  A2/4, '4E',  4/4,//  fe4/4, '4C',  3/4,//  male4/4, '4E',  4/4,//  deer8/4, '4D',  6/4,//  Re [Ray]6/4, '4E',  2/4,//  a2/4, '4F',  1/4,//  drop2/4, '4F',  1/4,//  of2/4, '4E',  2/4,//  gold2/4, '4D',  2/4,//  en2/4, '4F',  8/4,//  sun16/4, '4E',  6/4,//  Mi [Me]6/4, '4F',  2/4,//  a2/4, '4G',  5/4,//  name6/4, '4E',  2/4,//  I 2/4, '4G',  4/4,//  call4/4, '4E',  3/4,//  my4/4, '4G',  6/4,//  self8/4, '4F',  6/4,//  Fa[r]6/4, '4G',  2/4,//  a2/4, '4A',  1/4,//  long2/4, '4A',  1/4,//  long2/4, '4G',  2/4,//  way2/4, '4F',  2/4,//  to2/4, '4A',  8/4,//  run16/4, '4G',  6/4,//  So [Sew]6/4, '4C',  2/4,//  a2/4, '4D',  2/4,//  nee2/4, '4E',  2/4,//  dle2/4, '4F',  2/4,//  pull2/4, '4G',  2/4,//  ing2/4, '4A', 12/4,//  thread16/4, '4A',  6/4,//  La6/4, '4D',  2/4,//  a2/4, '4E',  2/4,//  note2/4, '4F',  2/4,//  to2/4, '4G',  2/4,//  fol2/4, '4A',  2/4,//  low2/4, '4B', 12/4,//  So16/4, '4B',  6/4,//  Ti [Tea]6/4, '4E',  2/4,//  a2/4, '4F',  2/4,//  drink2/4, '4G',  2/4,//  with2/4, '4A',  2/4,//  jam2/4, '4B',  2/4,//  and2/4, '5C', 12/4,//  bread12/4, '4A',  1/4,//  That2/4, '4A',  1/4,//  will2/4, '4A',  3/4,//  bring4/4, '4F',  3/4,//  us4/4, '4B',  3/4,//  back4/4, '4G',  3/4,//  to4/4, '5C',  8/4,//  Do4/4, '3C', 1/16,1/16, '3D', 1/16,1/16, '3C', 1/16,1/16, '3E', 1/16,1/16, '3F', 1/16,1/16, '3G', 1/16,1/16, '3A', 1/16,1/16, '3B', 1/16,1/16, '4C', 1/16,1/16, '4D', 1/16,1/16, '4C', 1/16,1/16, '4E', 1/16,1/16, '4F', 1/16,1/16, '4G', 1/16,1/16, '4A', 1/16,1/16, '4B', 1/16,1/16, '5C',  8/4,2/4, '4C',  6/4,2/4, '3C',  4/4,],harmony = [4/4, '3C', 1/4,//  Intro measures...4/4, '3G', 1/4,4/4, '3C', 1/4,4/4, '3G', 1/4,4/4, '3C', 1/4,4/4, '3G', 1/4,4/4, '3C', 1/4,4/4, '3G', 1/4,4/4, '3C', 2/4,//  Do[e]4/4, '3G', 2/4,4/4, '3C', 2/4,4/4, '3G', 2/4,4/4, '3C', 2/4,4/4, '3G', 2/4,4/4, '3C', 2/4,4/4, '3G', 2/4,4/4, '3F', 2/4,//  Re [Ray]4/4, '3D', 2/4,4/4, '3F', 2/4,4/4, '3D', 2/4,4/4, '3F', 2/4,4/4, '3D', 2/4,4/4, '3F', 2/4,4/4, '3D', 2/4,4/4, '3G', 2/4,//  Mi [Me]4/4, '3E', 2/4,4/4, '3G', 2/4,4/4, '3E', 2/4,4/4, '3G', 2/4,4/4, '3E', 2/4,4/4, '3G', 2/4,2/4, '3F', 2/4,2/4, '3E', 2/4,2/4, '3D', 2/4,2/4, '3F', 2/4,//  Fa[r]4/4, '3D', 2/4,4/4, '3F', 2/4,4/4, '3D', 2/4,4/4, '3F', 2/4,4/4, '3D', 2/4,     4/4, '3G', 2/4,2/4, '3F', 2/4,2/4, '3E', 2/4,2/4, '3D', 2/4,2/4, '3C', 2/4,2/4, '3D', 2/4,//  So [Sew]2/4, '3E', 2/4,2/4, '3F', 2/4,2/4, '3G', 2/4,2/4, '3A', 2/4,2/4, '3B', 2/4,2/4, '4C', 2/4,2/4, '3C', 2/4,2/4, '3D', 2/4,2/4, '3E', 2/4,2/4, '3F', 2/4,2/4, '3G', 2/4,2/4, '3A', 2/4,2/4, '3B', 2/4,2/4, '4C', 2/4,2/4, '3C', 2/4,//  La2/4, '3D', 2/4,2/4, '3E', 2/4,2/4, '3F', 2/4,2/4, '3G', 2/4,2/4, '3A', 2/4,2/4, '3B', 2/4,2/4, '4C', 4/4,4/4, '3B', 2/4,2/4, '3A', 2/4,2/4, '3G', 2/4,2/4, '3F', 2/4,2/4, '3E', 2/4,2/4, '3D', 2/4,2/4, '3C', 4/4,//  End of La / beginning of Ti4/4, '3D', 2/4,2/4, '3E', 2/4,2/4, '3F', 2/4,2/4, '3G', 2/4,2/4, '3A', 2/4,2/4, '3B', 2/4,2/4, '4C', 4/4,4/4, '3B', 2/4,2/4, '3A', 2/4,2/4, '3G', 2/4,2/4, '3F', 2/4,2/4, '3E', 2/4,2/4, '3D', 2/4,2/4, '3C', 2/4,2/4, '3G', 2/4,//  When2/4, '3C', 2/4,//  you2/4, '3A', 2/4,//  know2/4, '3F', 2/4,//  the2/4, '3E', 2/4,//  notes2/4, '3C', 2/4,//  to2/4, '3D', 4/4,//  sing4/4, '3G', 2/4,//  you2/4, '3C', 2/4,//  can2/4, '3A', 2/4,//  sing2/4, '3B', 2/4,//  most2/4, '4C', 2/4,//  an2/4, '4D', 2/4,//  y2/4, '4C', 4/4,//  thing]this.scoreUnload()this.scoreLoad( melody )this.scoreLoad( harmony )
}
BEEP.Instrument.prototype.scoreLoadHSB = function(){var guitar = [4/4, '3A♭', 3/4,4/4, '4E♭', 3/4,4/4, '4A♭', 3/4,4/4, '4E♭', 3/4,]this.scoreUnload()this.scoreLoad( guitar )
}

用JS生成声音,实现钢琴演奏相关推荐

  1. js模拟钢琴演奏代码js特效

    下载地址 js模拟钢琴演奏代码特效,多功能网页钢琴,支持多种风格选择,音程调节,键盘控制钢琴演奏特效. dd:

  2. 【天光学术】音乐教育论文:钢琴演奏触键在不同音乐时期的特征分析(节选)

    于亚男 摘 要:在钢琴演奏过程中,触键这一问题是较为重要的技术性问题,也是钢琴演奏学习过程中需要学习的基础,钢琴本身音质.音色等多方面因素都会直接对钢琴触键造成影响.在钢琴演奏过程中,演奏效果.触键以 ...

  3. pHp封装成vue,vue.js生成条形码的方法

    本文主要和大家分享vue.js生成条形码的方法,主要以代码的形式和大家分享,希望能帮助到大家. 1.下载插件npm install @xkeshi/vue-barcode //下载条形码插件 2.在m ...

  4. js生成[n,m]的随机数

    一.预备知识 Math.ceil();  //向上取整. Math.floor();  //向下取整. Math.round();  //四舍五入. Math.random();  //0.0 ~ 1 ...

  5. 钢琴演奏:舒伯特的小夜曲 --- 女儿的钢琴学习

    舒伯特的小夜曲 --- 2010.10.10 (星期日) --- 选择这首曲目去学校演奏 舒伯特的小夜曲 --- 2010.09.04 SUPERSEDED 找到好的钢琴老师 .. "师者传 ...

  6. LeaFlet学习之结合turf.js生成简单的等值线demo

    本文主要结合turf.js生成等值线俺,进行展示效 一.放张图: 二.全部源码 <!DOCTYPE html> <html xmlns="http://www.w3.org ...

  7. php mysql随机数不重复,js生成不重复的随机数

    这篇文章主要为大家详细介绍了js生成不重复的随机数,具有一定的参考价值,可以用来参考一下. 感兴趣的小伙伴,下面一起跟随512笔记的小编罗X来看看吧. JS代码如下: /** * * @param * ...

  8. 真的了解js生成随机数吗

    由js生成一切随机数的基础都是Math.random(),这个方法比较特别,生成的随机数落在的区间是[0,1),进行一次操作的话,js只能生成一个类似于[n,m)这样,左闭右开的区间.所以当有一些特殊 ...

  9. vue生成静态js文件_如何立即使用Vue.js生成静态网站

    vue生成静态js文件 by Ondřej Polesný 通过OndřejPolesný 如何立即使用Vue.js生成静态网站 (How to generate a static website w ...

  10. js生成一周内的日期+周几

    (如有错敬请指点,以下是我工作中遇到并且解决的问题) 效果有两种: 两者区别是 1.第一天(今天)显示今日 2.第一天(今天)显示周几 (第一个图是在手机上显示的效果,第二个是PC网页上显示的效果) ...

最新文章

  1. python3.x下 smtp发送html邮件和附件
  2. Java知多少(12)运算符
  3. JavaScript对象、JSON对象、JSON字符串的区别
  4. python __del__
  5. 科学技术究竟有没有国界?独家专访 IEEE 高级会员张海霞教授
  6. leetcode @python 123. Best Time to Buy and Sell Stock III
  7. windows7+fedora16双系统安装
  8. 什么是工业物联网?与工业互联网有什么区别
  9. bzoj 3772: 精神污染 (主席树+dfs序)
  10. Linux中make, make install命令分别是什么,用法?
  11. 大型网络之BGP路由通告路由传递配置
  12. mysql入门_高洛峰_简介_linux安装_远程连接配置_sql语句初始
  13. C++析取器在代码自动化测试中的应用
  14. js调用(前/后)摄像头,截取照片,关闭摄像头
  15. CentOS升级openssl修复部分ssl漏洞
  16. 重叠头像最简单实现(RecyclerView实现)
  17. (一)傅里叶变换:傅里叶级数(Fourier Series)
  18. apple iMac一体机 装双系统 实战! (Apple +Win 7 64bit)Good
  19. [转载] 机器学习数据集统计系列(二)
  20. TechSmith Camtasia 2019 特别版 Mac 最强大的屏幕录像工具

热门文章

  1. vue+element 实现时间选择器切换周月选择
  2. 天降公网ip | 你也许已经有公网IP了
  3. linux批量化删除以某某结尾命令
  4. 【PHP】面试经历总结之——新浪微博
  5. The Little Schemer Fourth Edition,笔记01
  6. 一个好玩的c++小游戏 另外一个是木马病毒
  7. linux 安装codeql环境 (二)codeql database create通过报错分析其流程
  8. 网站使用微信网页授权,qq登录
  9. VMware虚拟机操作汇总
  10. AddressBook、AddressBookUI、Contacts、ContactsUI 通讯录操作