Skip to content

Commit

Permalink
Add more user-friendly functions
Browse files Browse the repository at this point in the history
* loadInstrumentUsingProvider
* loadInstrumentsUsingProvider
* midiNote

These allow the user to choose a different provider from the Gleitzman
site other than MusynKite and also offer a simple constructor for
MidiNote.
  • Loading branch information
newlandsvalley committed Jun 16, 2022
1 parent cf09f48 commit 5282a9b
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 34 deletions.
14 changes: 5 additions & 9 deletions docs/GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ import Effect.Console (log)
import Data.Time.Duration (Milliseconds(..))
import Data.Maybe (Maybe(..))
import Data.Newtype (wrap)
import Audio.SoundFont (Instrument, MidiNote, loadRemoteSoundFonts, playNote, playNotes)
import Audio.SoundFont (Instrument, MidiNote, loadRemoteSoundFonts, midiNote, playNote, playNotes)
import Data.Midi.Instrument (InstrumentName(..))
import Web.DOM.ParentNode (querySelector)
import Web.Event.EventTarget (EventTarget, addEventListener, eventListener)
Expand Down Expand Up @@ -91,12 +91,8 @@ playNotesExample instruments = do
duration_ <- liftEffect $ playNote instruments noteSampleA
pure unit
note :: Int -> Int -> Number -> Number -> Number -> MidiNote
note channel id timeOffset duration gain =
{ channel, id, timeOffset, duration, gain }
noteSampleA :: MidiNote
noteSampleA = note 0 57 0.0 1.0 1.0
noteSampleA = midiNote 0 57 0.0 1.0 1.0
```

## Playing Chords
Expand All @@ -105,13 +101,13 @@ To play a chord, you need to supply an array of ```MidiNote``` at the appropriat

```purs
noteSampleA :: MidiNote
noteSampleA = note 0 57 0.0 1.0 1.0
noteSampleA = midiNote 0 57 0.0 1.0 1.0
noteSampleCAt :: Number -> MidiNote
noteSampleCAt offset = note 0 60 offset 1.0 1.0
noteSampleCAt offset = midiNote 0 60 offset 1.0 1.0
noteSampleEAt :: Number -> MidiNote
noteSampleEAt offset = note 0 64 offset 1.0 1.0
noteSampleEAt offset = midiNote 0 64 offset 1.0 1.0
chord :: Array MidiNote
chord =
Expand Down
23 changes: 10 additions & 13 deletions example/src/Main.purs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Data.Unfoldable (replicate)
import Audio.SoundFont (Instrument
, MidiNote
, loadRemoteSoundFonts
, midiNote
, playNote
, playNotes)
import Audio.SoundFont.Melody (playMelody)
Expand All @@ -23,27 +24,23 @@ import Web.HTML.HTMLDocument (toParentNode)
import Web.HTML.Window (document)
import Unsafe.Coerce (unsafeCoerce)

note :: Int -> Int -> Number -> Number -> Number -> MidiNote
note channel id timeOffset duration gain =
{ channel, id, timeOffset, duration, gain }

noteSampleA :: MidiNote
noteSampleA = note 0 57 0.0 0.5 1.0
noteSampleA = midiNote 0 57 0.0 0.5 1.0

noteSampleC :: MidiNote
noteSampleC = note 0 60 0.0 0.5 1.0
noteSampleC = midiNote 0 60 0.0 0.5 1.0

noteSampleE :: MidiNote
noteSampleE = note 0 64 0.0 0.5 1.0
noteSampleE = midiNote 0 64 0.0 0.5 1.0

notesSample :: Int -> Array MidiNote
notesSample channel =
[ note channel 60 1.0 0.5 1.0
, note channel 62 1.5 0.5 1.0
, note channel 64 2.0 0.5 1.0
, note channel 65 2.5 0.5 1.0
, note channel 67 3.0 1.5 1.0
, note channel 71 3.0 1.5 1.0
[ midiNote channel 60 1.0 0.5 1.0
, midiNote channel 62 1.5 0.5 1.0
, midiNote channel 64 2.0 0.5 1.0
, midiNote channel 65 2.5 0.5 1.0
, midiNote channel 67 3.0 1.5 1.0
, midiNote channel 71 3.0 1.5 1.0
]

main :: Effect Unit
Expand Down
62 changes: 50 additions & 12 deletions src/Audio/SoundFont.purs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Audio.SoundFont
( AudioBuffer
( module Exports
, AudioBuffer
, Instrument
, InstrumentChannels
, MidiNote
Expand All @@ -10,8 +11,11 @@ module Audio.SoundFont
, logLoadResource
, loadInstrument
, loadInstruments
, loadInstrumentUsingProvider
, loadInstrumentsUsingProvider
, loadRemoteSoundFonts
, loadPianoSoundFont
, midiNote
, playNote
, playNotes
, instrumentChannels
Expand All @@ -20,7 +24,8 @@ module Audio.SoundFont
import Affjax.Web (defaultRequest, request)
import Affjax.ResponseFormat as ResponseFormat
import Audio.SoundFont.Decoder (midiJsToNoteMap, debugNoteIds)
import Audio.SoundFont.Gleitz (RecordingFormat(..), SoundFontType(..), gleitzUrl)
import Audio.SoundFont.Gleitz (RecordingFormat(..), gleitzUrl)
import Audio.SoundFont.Gleitz (SoundFontType(..)) as Exports
import Control.Parallel (parallel, sequential)
import Data.Array (index, last, mapWithIndex)
import Data.ArrayBuffer.Types (Uint8Array)
Expand Down Expand Up @@ -86,38 +91,52 @@ foreign import isWebAudioEnabled
foreign import setNoteRing
:: Number -> Effect Unit

-- | load a bunch of soundfonts from the Gleitzmann server
-- | load a bunch of soundfonts from the Gleitzmann server using the MusyngKite provider
loadRemoteSoundFonts
:: Array InstrumentName
-> Aff (Array Instrument)
loadRemoteSoundFonts =
loadInstruments Nothing

-- | load the piano soundfont from a relative directory on the local server
-- | using the MusyngKite provider
loadPianoSoundFont
:: String
-> Aff Instrument
loadPianoSoundFont localDir =
loadInstrument (Just localDir) AcousticGrandPiano

-- | load a single instrument SoundFont

-- | load a single instrument SoundFont using the ```MusyngKite``` provider
-- | The options are to load the soundfont from:
-- | Benjamin Gleitzman's server (default)
-- | Benjamin Gleitzman's server (default of Nothing)
-- | A directory from the local server if this is supplied

loadInstrument
:: Maybe String
-> InstrumentName
-> Aff Instrument
loadInstrument maybeLocalDir instrumentName = do
loadInstrument maybeLocalDir instrumentName =
loadInstrumentUsingProvider maybeLocalDir Exports.MusyngKite instrumentName

-- | load a single instrument SoundFont
-- | The options are to load the soundfont from:
-- | Benjamin Gleitzman's server (default: Nothing) or
-- | A directory from the local server if this is supplied
-- | and to use your choice of SoundFont provider
loadInstrumentUsingProvider
:: Maybe String
-> Exports.SoundFontType
-> InstrumentName
-> Aff Instrument
loadInstrumentUsingProvider maybeLocalDir provider instrumentName = do
recordingFormat <- liftEffect prefferedRecordingFormat
let
url =
case maybeLocalDir of
Just localDir ->
localUrl instrumentName localDir recordingFormat
_ ->
gleitzUrl instrumentName MusyngKite recordingFormat
gleitzUrl instrumentName provider recordingFormat
res <- request $ defaultRequest
{ url = url, method = Left GET, responseFormat = ResponseFormat.string }

Expand All @@ -132,15 +151,34 @@ loadInstrument maybeLocalDir instrumentName = do
font <- traverse decodeAudioBuffer noteMap
pure (Tuple instrumentName font)

-- | load a bunch of instrument SoundFonts (in parallel)
-- | load a bunch of instrument SoundFonts (in parallel) using the MusyngKite provider.
-- | again with options to load either locally or remotely
-- | from Benjamin Gleitzman's server
loadInstruments
:: Maybe String
-> Array InstrumentName
-> Aff (Array Instrument)
loadInstruments maybeLocalDir instrumentNames =
loadInstrumentsUsingProvider maybeLocalDir Exports.MusyngKite instrumentNames

-- | load a bunch of instrument SoundFonts (in parallel)
-- | with options to load either locally or remotely
-- | from Benjamin Gleitzman's server
-- | and to use your choice of SoundFont provider
loadInstrumentsUsingProvider
:: Maybe String
-> Exports.SoundFontType
-> Array InstrumentName
-> Aff (Array Instrument)
loadInstruments maybeLocalDir instrumentNames =
sequential $ traverse (\name -> parallel (loadInstrument maybeLocalDir name)) instrumentNames
loadInstrumentsUsingProvider maybeLocalDir provider instrumentNames =
sequential $ traverse
(\name -> parallel (loadInstrumentUsingProvider maybeLocalDir provider name)) instrumentNames


-- | Construct a MidiNote
midiNote :: Int -> Int -> Number -> Number -> Number -> MidiNote
midiNote channel id timeOffset duration gain =
{ channel, id, timeOffset, duration, gain }

foreign import decodeAudioBufferImpl
:: Uint8Array -> EffectFnAff AudioBuffer
Expand Down Expand Up @@ -204,7 +242,7 @@ logLoadResource
-> Effect (Fiber Unit)
logLoadResource instrument =
let
url = gleitzUrl instrument MusyngKite OGG
url = gleitzUrl instrument Exports.MusyngKite OGG
in
launchAff $ do
res <- request $ defaultRequest
Expand Down

0 comments on commit 5282a9b

Please sign in to comment.