A Haskell library and command-line tool for generating MusicXML files through functional composition and music theory.
- Type-safe music representation with Haskell's strong type system
- Automatic scale generation with proper accidentals and octave handling
- MusicXML output compatible with notation software (MuseScore, Finale, Sibelius)
- Modular architecture with separate core theory and XML rendering libraries
- Comprehensive testing with unit, property-based, and golden file tests
- Command-line interface for quick music generation
# Clone the repository
git clone https://github.com/tim-br/hymn
cd hymn
# Build with Stack
stack build
# Run the CLI
stack exec hymn -- generate# Generate a major scale
stack exec hymn -- generate
# Show version
stack exec hymn -- versionThis will create a scale.musicxml file containing an E major scale that you can open in any MusicXML-compatible notation software.
import Hymn.Types
import Hymn.Theory
import Hymn.Render
-- Generate D Major scale
let dMajorScale = generateMajorScale D 4
-- Result: [D4, E4, F#4, G4, A4, B4, C#5, D5]
-- Create a complete MusicXML score
let attributes = Attributes 4 (Just (Key 0 Nothing)) (Just (TimeSig 4 4)) (Just (Clef "G" 2))
measure = Measure 1 (Just attributes) (map MNote dMajorScale)
part = Part "P1" [measure]
score = ScorePartwise "3.1" [ScorePart "P1" "D Major Scale"] [part]
-- Export to MusicXML
writeMusicXML "d-major.xml" score-- Create a middle C quarter note
let middleC = Note
{ pitch = Just (Pitch C Nothing 4)
, duration = 4
, voice = 1
, noteType = Just Quarter
, accidental = Nothing
, rest = False
, tieStart = False
, tieStop = False
}-- F# (F sharp)
let fSharp = Pitch F (Just 1) 4
-- Bb (B flat)
let bFlat = Pitch B (Just (-1)) 4
-- The library automatically generates correct accidentals for scales
let fMajorScale = generateMajorScale F 4 -- Includes Bb automaticallyHymn is built with a modular architecture:
hymn/
├── hymn-core/ # Core music theory types and functions
│ ├── src/Hymn/
│ │ ├── Types.hs # Data types for musical concepts
│ │ └── Theory.hs # Scale generation and music theory
│ └── hymn-core.cabal
├── hymn-xml/ # MusicXML rendering
│ ├── src/Hymn/
│ │ └── Render.hs # MusicXML generation
│ └── hymn-xml.cabal
├── app/ # Command-line interface
│ └── Hymn/
│ ├── Main.hs
│ └── CLI.hs
├── test/ # Comprehensive test suite
│ ├── Spec.hs # Unit tests
│ ├── Properties.hs # Property-based tests
│ ├── Integration.hs # Integration tests
│ └── Golden.hs # Golden file regression tests
└── hymn.cabal # Main executable
-- Pitch representation
data Pitch = Pitch
{ step :: PitchStep -- A, B, C, D, E, F, G
, alter :: Maybe Int -- Sharps (+1) or flats (-1)
, octave :: Int -- Octave number
}
-- Note with timing and voice information
data Note = Note
{ pitch :: Maybe Pitch
, duration :: Int
, voice :: Int
, noteType :: Maybe NoteType
, accidental :: Maybe Accidental
, rest :: Bool
, tieStart :: Bool
, tieStop :: Bool
}Currently supports major scales with:
- Automatic accidental calculation based on key signature
- Proper octave handling when crossing octave boundaries
- All 12 major scales (C, G, D, A, E, B, F#, Db, Ab, Eb, Bb, F)
-- Examples of different scales
let cMajor = generateMajorScale C 4 -- No accidentals
let gMajor = generateMajorScale G 4 -- F#
let fMajor = generateMajorScale F 4 -- Bb
let dMajor = generateMajorScale D 4 -- F#, C#The library is designed to be easily extended with:
- Minor scales, modes, and exotic scales
- Chord generation and progressions
- Rhythm patterns and time signatures
- Advanced harmony and counterpoint
Hymn includes comprehensive testing:
# Run all tests
stack test
# Run specific test categories
stack test --test-arguments="--match=\"Music Theory\""
stack test --test-arguments="--match=\"XML Rendering\""
# Run with coverage
stack test --coverage- Unit Tests - Test individual functions and components
- Property Tests - Verify mathematical properties (e.g., scales always have 8 notes)
- Integration Tests - Test complete workflows and file generation
- Golden Tests - Regression testing against reference XML output
import Hymn.Types
import Hymn.Theory
import Hymn.Render
generateAllMajorScales :: IO ()
generateAllMajorScales = do
let scales = [C, D, E, F, G, A, B]
mapM_ generateScaleFile scales
where
generateScaleFile step = do
let scale = generateMajorScale step 4
attributes = Attributes 4 (Just (Key 0 Nothing)) (Just (TimeSig 4 4)) (Just (Clef "G" 2))
measure = Measure 1 (Just attributes) (map MNote scale)
part = Part "P1" [measure]
score = ScorePartwise "3.1" [ScorePart "P1" (show step ++ " Major")] [part]
filename = show step ++ "-major.xml"
writeMusicXML filename score
putStrLn $ "Generated " ++ filenamecreateMelody :: [PitchStep] -> [Note]
createMelody steps =
map (\step -> Note (Just (Pitch step Nothing 4)) 4 1 (Just Quarter) Nothing False False False) steps
-- Twinkle Twinkle Little Star (first phrase)
let melody = createMelody [C, C, G, G, A, A, G]- Noteflight (web-based) ✅
- Verovio (with proper MusicXML structure) ✅
- MusicXML 3.1 compliant
- Proper UTF-8 encoding
- Valid XML structure with required elements
# Prerequisites: Stack (Haskell build tool)
curl sSL https://get.haskellstack.org/ | sh
# Clone and build
git clone https://github.com/tim-br/hymn
cd hymn
stack setup
stack build
stack test- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Add tests for your changes
- Ensure all tests pass (
stack test) - Commit your changes (
git commit -am 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
The modular design makes it easy to add new functionality:
- New scales/modes: Add to
Hymn.Theory - New MusicXML elements: Extend types in
Hymn.Typesand rendering inHymn.Render - New commands: Add to the CLI in
Hymn.CLI
- Inspired by the beauty of functional programming applied to music theory
- MusicXML specification by MakeMusic Inc.
- The Haskell community for excellent libraries and tools
"Music is the pleasure the human mind experiences from counting without being aware that it is counting." - Gottfried Leibniz