Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

download generated sequence as midi file #7

Merged
merged 6 commits into from
Jun 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion elm-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"dependencies": {
"elm-community/random-extra": "2.0.0 <= v < 3.0.0",
"elm-lang/core": "5.0.0 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0"
"elm-lang/html": "2.0.0 <= v < 3.0.0",
"newlandsvalley/elm-binary-base64": "1.0.2 <= v < 2.0.0",
"newlandsvalley/elm-comidi": "3.0.0 <= v < 4.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}
51 changes: 39 additions & 12 deletions src/Main.elm
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
module Main exposing (..)

import Html exposing (Html, text, div, h1, button, p, i)
import Html exposing (Html, text, div, h1, button, p, i, a)
import Html.Events exposing (onMouseDown, onMouseUp, onClick)
import Html.Attributes exposing (class)
import Html.Attributes exposing (class, href, download, downloadAs)
import Ports exposing (..)
import Types exposing (..)
import Random exposing (generate)
import Array exposing (Array, fromList, toList, map)
import Random.Array exposing (shuffle)
import Midi.Types as Midi
import Midi.Generate exposing (recording)
import BinaryBase64 exposing (encode, decode)


---- MODEL ----
Expand Down Expand Up @@ -45,10 +48,10 @@ pitchToSampleUrlMapping (Pitch note accidental octave) =

loadPianoSamples : Cmd msg
loadPianoSamples =
[ Pitch C Nothing 4
, Pitch D (Just Sharp) 4
, Pitch F (Just Sharp) 4
, Pitch A Nothing 4
[ Pitch C Nothing middleOctave
, Pitch D (Just Sharp) middleOctave
, Pitch F (Just Sharp) middleOctave
, Pitch A Nothing middleOctave
]
|> List.map pitchToSampleUrlMapping
|> loadSamples
Expand All @@ -59,13 +62,36 @@ generate12ToneRow =
Random.generate RowGenerated (Random.Array.shuffle (chromaticScale 4))


flatten : List (List a) -> List a
flatten list =
List.foldr (++) [] list


zipWithIndex : List a -> List ( a, Int )
zipWithIndex list =
List.range 0 (List.length list) |> List.map2 (,) list


toMidi : Array Pitch -> List Midi.Byte
toMidi row =
row
|> Array.toList
|> List.map toMidiNumber
|> zipWithIndex
|> List.map
(\( midiNumber, i ) ->
[ ( 0, Midi.NoteOn 0 midiNumber 64 )
, ( 2, Midi.NoteOff 0 midiNumber 0 )
]
)
|> flatten
|> Midi.SingleTrack 4
|> recording


init : ( Model, Cmd Msg )
init =
let
cmd =
Cmd.batch [ loadPianoSamples, generate12ToneRow ]
in
( Nothing, cmd )
( Nothing, Cmd.batch [ loadPianoSamples, generate12ToneRow ] )



Expand Down Expand Up @@ -93,7 +119,7 @@ update msg model =
( Just (Stopped row), Cmd.none )

GenerateNew12ToneRow ->
( Nothing, Cmd.batch [ stopSequence (), generate12ToneRow ] )
( model, Cmd.batch [ stopSequence (), generate12ToneRow ] )

TogglePlay ->
case model of
Expand Down Expand Up @@ -128,6 +154,7 @@ rowWithControls row icon =
, p []
[ button [ onClick TogglePlay ] [ i [ class icon ] [] ]
, generateButton
, a [ href ("data:audio/midi;base64," ++ (encode (toMidi row))), downloadAs "luigi.midi", class "button" ] [ i [ class "fas fa-download" ] [] ]
]
]

Expand Down
52 changes: 52 additions & 0 deletions src/Types.elm
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ type alias Model =
Maybe Row


middleOctave : Int
middleOctave =
4


chromaticScale : Octave -> Array Pitch
chromaticScale octave =
Array.fromList
Expand All @@ -59,3 +64,50 @@ chromaticScale octave =
, Pitch A (Just Sharp) octave
, Pitch B Nothing octave
]


type alias MidiNumber =
Int


toMidiNumber : Pitch -> MidiNumber
toMidiNumber (Pitch letter accidental octave) =
(octave + 1) * 12 + (letterToInt letter) + (accidentalToInt accidental)


letterToInt : Letter -> Int
letterToInt letter =
case letter of
C ->
0

D ->
2

E ->
4

F ->
5

G ->
7

A ->
9

B ->
11


accidentalToInt : Maybe Accidental -> Int
accidentalToInt accidental =
case accidental of
Just Sharp ->
1

Just Flat ->
-1

Nothing ->
0
5 changes: 3 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ app.ports.loadSamples.subscribe(function(pitchToSampleUrlMapping){

app.ports.startSequence.subscribe(function(seq){
var noteLength = "8n"
sequence = new Sequence(function(time, note){
var subdivision = "8n"
sequence = new Sequence(function(_, note){
sampler.triggerAttackRelease(note, noteLength)
}, seq, noteLength);
}, seq, subdivision);
Transport.start()
sequence.start();
});
Expand Down
2 changes: 1 addition & 1 deletion src/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ body {
color: #DFDFDF;
}

button {
button, .button {
border: 1px solid #666666;
background-color: #858585;
user-select: none;
Expand Down
22 changes: 14 additions & 8 deletions tests/Tests.elm
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@ module Tests exposing (..)

import Test exposing (..)
import Expect
import Types exposing (..)


-- Check out http://package.elm-lang.org/packages/elm-community/elm-test/latest to learn more about testing in Elm!


all : Test
all =
describe "A Test Suite"
[ test "Addition" <|
describe "Pitch to MIDI number"
[ test "A0 should be 21" <|
\_ ->
Expect.equal (toMidiNumber (Pitch A Nothing 0)) 21
, test "C8 should be 108" <|
\_ ->
Expect.equal (toMidiNumber (Pitch C Nothing 8)) 108
, test "C4 should be 60" <|
\_ ->
Expect.equal 10 (3 + 7)
, test "String.left" <|
Expect.equal (toMidiNumber (Pitch C Nothing 4)) 60
, test "F#5 should be 78" <|
\_ ->
Expect.equal "a" (String.left 1 "abcdefg")
, test "This test should fail" <|
Expect.equal (toMidiNumber (Pitch F (Just Sharp) 5)) 78
, test "F#5 should be 78" <|
\_ ->
Expect.fail "failed as expected!"
Expect.equal (toMidiNumber (Pitch F (Just Sharp) 5)) 78
]