gohar

package module
v0.0.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 16, 2025 License: MIT Imports: 7 Imported by: 0

Documentation

Overview

Package gohar implements data types and algorithms to manipulate musical concepts, such as notes (A, Bb, C#...), intervals (major sitxh, perfect fifth...), chords and scales.

Index

Constants

View Source
const (
	AltSharp       = "♯"
	AltFlat        = "♭"
	AltNatural     = "♮"
	AltDoubleSharp = "𝄪"
	AltDoubleFlat  = "𝄫"
)

Variables

View Source
var (
	ErrBufferOverflow      = errors.New("buffer overflow")
	ErrNilBuffer           = errors.New("nil buffer")
	ErrInvalidPitchClass   = errors.New("invalid pitch class")
	ErrInvalidAlteration   = errors.New("invalid alteration")
	ErrUnknownScalePattern = errors.New("unknown scale pattern")
	ErrInvalidDegree       = errors.New("invalid degree")
)
View Source
var (
	IntUnisson           = Interval{}
	IntMinorSecond       = Interval{1, 1}
	IntMajorSecond       = Interval{1, 2}
	IntAugmentedSecond   = Interval{1, 3}
	IntMinorThird        = Interval{2, 3}
	IntMajorThird        = Interval{2, 4}
	IntDiminishedFourth  = Interval{3, 4}
	IntPerfectFourth     = Interval{3, 5}
	IntAugmentedFourth   = Interval{3, 6}
	IntDiminishedFifth   = Interval{4, 6}
	IntPerfectFifth      = Interval{4, 7}
	IntAugmentedFifth    = Interval{4, 8}
	IntMinorSixth        = Interval{5, 8}
	IntMajorSixth        = Interval{5, 9}
	IntDiminishedSeventh = Interval{6, 9}
	IntAugmentedSixth    = Interval{5, 10}
	IntMinorSeventh      = Interval{6, 10}
	IntMajorSeventh      = Interval{6, 11}
	IntOctave            = Interval{7, 12}
	IntMinorNinth        = Interval{8, 13}
	IntMajorNinth        = Interval{8, 14}
	IntAugmentedNinth    = Interval{8, 15}
	IntMinorTenth        = Interval{9, 15}
	IntMajorTenth        = Interval{9, 16}
	IntPerfectEleventh   = Interval{10, 17}
	IntAugmentedEleventh = Interval{10, 18}
	IntMinorThirteenth   = Interval{12, 20}
	IntMajorThirteenth   = Interval{12, 21}
	IntMajorFourteenth   = Interval{13, 23}
)
View Source
var (
	LocaleFrench = Locale{
		NoteNames: []string{"do", "ré", "mi", "fa", "sol", "la", "si"},
		ScaleNames: map[ScalePattern]string{
			ScalePatternMajor:               "majeur",
			ScalePatternMelodicMinor:        "mineur mélodique",
			ScalePatternHarmonicMinor:       "mineur harmonique",
			ScalePatternHarmonicMajor:       "majeur harmonique",
			ScalePatternDoubleHarmonicMajor: "majeur double harmonique",
		},
	}

	LocaleEnglish = Locale{
		NoteNames: []string{"c", "d", "e", "f", "g", "a", "b"},
		ScaleNames: map[ScalePattern]string{
			ScalePatternMajor:               "major",
			ScalePatternMelodicMinor:        "melodic minor",
			ScalePatternHarmonicMinor:       "harmonic minor",
			ScalePatternHarmonicMajor:       "harmonic major",
			ScalePatternDoubleHarmonicMajor: "double harmonic major",
		},
	}

	CurrentLocale = &LocaleEnglish
)
View Source
var (
	NoteA = Note{PitchClassA, 0}
	NoteB = Note{PitchClassB, 0}
	NoteC = Note{PitchClassC, 0}
	NoteD = Note{PitchClassD, 0}
	NoteE = Note{PitchClassE, 0}
	NoteF = Note{PitchClassF, 0}
	NoteG = Note{PitchClassG, 0}
)
View Source
var (
	ErrCannotParseNote   = errors.New("cannot parse note")
	ErrUnknownAlteration = errors.New("unknown alteration")
)
View Source
var ErrLocaleNotSet = errors.New("gohar.CurrentLocale is not set")

Functions

func NoteName

func NoteName(note PitchClass) (string, error)

NoteName returns the Note's name in the current locale.

ErrLocaleNotSet is returned if the package's locale isn't set.

func PitchesWithClasses added in v0.0.5

func PitchesWithClasses(from, to Pitch, pcs []PitchClass) iter.Seq2[Pitch, PitchClass]

PitchesWithClasses iterates over all pitches in the range [from, to] that belong to given pitchclasses, in ascending order.

func ScaleName

func ScaleName(scale Scale) (string, error)

ScaleName returns the Scale's name in the current locale.

ErrLocaleNotSet is returned if the package's locale isn't set.

func ScalePatternName

func ScalePatternName(pattern ScalePattern) (string, error)

ScalePatternName returns the ScalePattern's name in the current locale.

ErrLocaleNotSet is returned if the package's locale isn't set.

Types

type ChordPattern

type ChordPattern uint32

A chord pattern is represented bitwise as a 24-bit number. Each bit corresponds to a discrete pitch, within two 12-pitch octaves. When unpacked, a chord is preferably laid out using the first octave for the base chord (triad or tetrad) and the second octave for extensions (9th and above).

This representation keeps the chord in a format that is efficiently computable, especially for set operations such as testing whether a chord contains certain degrees or extensions, whilst remaining close to most consistent naming conventions.

const (
	ChordPatternMajor       ChordPattern = 0b000010010001 // Major triad (C: C E G)
	ChordPatternMinor       ChordPattern = 0b000010001001 // Minor triad (Cm: C Eb G)
	ChordPatternDiminished  ChordPattern = 0b000001001001 // Diminished triad (C dim: C Eb Gb)
	ChordPatternAugmented   ChordPattern = 0b000100010001 // Augmented triad (C aug: C E G#)
	ChordPatternSus4        ChordPattern = 0b000010100001 // sus4 chord (Csus4: C F G)
	ChordPatternMajor7      ChordPattern = 0b100010010001 // Major 7 chord (CMaj7: C E G B)
	ChordPatternMajor7No5   ChordPattern = 0b100000010001 // Major 7 (omit 5) chord (CMaj7 No5: C E B)
	ChordPattern7           ChordPattern = 0b010010010001 // Dominant 7 chord (C7: C E G Bb)
	ChordPattern7No5        ChordPattern = 0b010000010001 // Dominant 7 (omit5) chord (C7 No5: C E Bb)
	ChordPatternMinor7      ChordPattern = 0b010010001001 // Minor 7 chord (Cm7: C Eb G Bb)
	ChordPatternMinor7No5   ChordPattern = 0b010000001001 // Minor 7 No5 chord (Cm7 No5: C Eb Bb)
	ChordPatternMinor7Flat5 ChordPattern = 0b010001001001 // Minor 7 b5 chord (Cm7 b5: C Eb Gb Bb)
	ChordPatternDiminished7 ChordPattern = 0b001001001001 // Diminished 7 chord (Cdim7: C Eb Gb Bbb)
)

func (ChordPattern) Add

func (c ChordPattern) Add(p Pitch) ChordPattern

Add adds a pitch to the chord. This has no effect if the pitch is already present.

func (ChordPattern) AsIntervals

func (c ChordPattern) AsIntervals() []Interval

func (ChordPattern) Contains

func (c ChordPattern) Contains(o ChordPattern) bool

Contains return if other is a subset of the current chord pattern.

func (ChordPattern) CountNotes

func (c ChordPattern) CountNotes() int

CountNotes counts the notes in the chord.

func (ChordPattern) HasAllDegrees

func (c ChordPattern) HasAllDegrees(pitches ...Pitch) bool

HasAllDegree returns true if the chord contains all of the pitches.

func (ChordPattern) HasAnyDegree

func (c ChordPattern) HasAnyDegree(pitches ...Pitch) bool

HasAnyDegree returns true if the chord contains any of the pitches.

func (ChordPattern) HasDegree

func (c ChordPattern) HasDegree(p Pitch) bool

HasDegree returns true if the chord contains given degree as a pitch.

func (ChordPattern) IntoIntervals

func (c ChordPattern) IntoIntervals(out []Interval) ([]Interval, error)

func (ChordPattern) Omit

func (c ChordPattern) Omit(p Pitch) ChordPattern

Omit removes a pitch from the chord. This has no effect if the pitch is already absent.

func (ChordPattern) String

func (c ChordPattern) String() string

String returns a string representation of the chord.

func (ChordPattern) Unpack

func (c ChordPattern) Unpack() ChordPattern

type Interval

type Interval struct {
	ScaleDiff int8
	PitchDiff Pitch
}

func (Interval) Down

func (i Interval) Down() Interval

type Locale

type Locale struct {
	NoteNames  []string
	ScaleNames map[ScalePattern]string
}

A Locale is responsible for producing string representations that suit the user's language and culture.

func (*Locale) NoteName

func (loc *Locale) NoteName(note PitchClass) (string, error)

NoteName returns the Note's name in the current locale.

func (*Locale) ScaleName

func (loc *Locale) ScaleName(scale Scale) (string, error)

ScaleName returns the Scale's name in the current locale.

func (*Locale) ScalePatternName

func (loc *Locale) ScalePatternName(pattern ScalePattern) (string, error)

ScalePatternName returns the ScalePattern's name in the current locale.

type Note

type Note struct {
	// PitchClass is the pitch class of the note
	PitchClass
	// Oct transposes the note up (when Oct > 0) or down (when Oct < 0) relative to the default octave (0).
	Oct int8
}

A Note is an abstract music note that can be converted to pitch.

Multiple notes can correspond to the same pitch (e.g.: E# and F). Notes that have the same pitch but different names are called "enharmonic".

func FindClosestNote

func FindClosestNote(pitch Pitch) Note

FindClosestNote returns the closest note with given pitch. When a pitch corresponds to an altered note ("black key"), it is always assumed to be the flattened note above it.

func NoteWithPitch

func NoteWithPitch(pc PitchClass, pitch Pitch) Note

NoteWithPitch builds a note with given base and any octaves and alterations needed so that the note has given pitch.

func ParseNote

func ParseNote(input string) (Note, error)

func (Note) DoubleFlat

func (n Note) DoubleFlat() Note

DoubleFlat makes the note "double flat".

func (Note) DoubleSharp

func (n Note) DoubleSharp() Note

DoubleSharp makes the note "double sharp".

func (Note) Flat

func (n Note) Flat() Note

Flat returns the note with an added flat.

func (Note) IsEnharmonic

func (n Note) IsEnharmonic(note Note) bool

IsEnharmonic returns true if both notes have the same pitch.

func (Note) IsHigherThan

func (n Note) IsHigherThan(note Note) bool

IsHigherThan returns true if the current note is higher than the argument.

func (Note) Name

func (n Note) Name() string

Name returns the note's name regardless of the octave.

func (Note) Octave

func (n Note) Octave(oct int8) Note

Octave transposes the note to desired octave.

func (Note) Pitch

func (n Note) Pitch() Pitch

Get the note's pitch.

This method panics if the note is malformed.

func (Note) Sharp

func (n Note) Sharp() Note

Sharp returns the note with an added sharp.

func (Note) String

func (n Note) String() string

String returns the full note as a string.

func (Note) Transpose

func (n Note) Transpose(i Interval) Note

Transpose returns the note transposed by given interval

type Pitch

type Pitch int8

A Pitch corresponds to a distinct vibration frequency. Pitches are represented as a signed integer value in the range [-127; 128]. This range is more than enough to model a piano keyboard, whose range is contained within [-50; 50].

Pitch unit is the semitone or half-step. An octave corresponds to 12 semitones.

By convention, pitch 0 corresponds to middle C on a piano keyboard.

const (
	PitchC            Pitch = 0
	PitchBSharp       Pitch = 0
	PitchDDoubleFlat  Pitch = 0
	PitchBDoubleSharp Pitch = 1
	PitchCSharp       Pitch = 1
	PitchDFlat        Pitch = 1
	PitchCDoubleSharp Pitch = 2
	PitchD            Pitch = 2
	PitchEDoubleFlat  Pitch = 2
	PitchDSharp       Pitch = 3
	PitchEFlat        Pitch = 3
	PitchDDoubleSharp Pitch = 4
	PitchE            Pitch = 4
	PitchFFlat        Pitch = 4
	PitchFDoubleFlat  Pitch = 4
	PitchESharp       Pitch = 5
	PitchF            Pitch = 5
	PitchGDoubleFlat  Pitch = 5
	PitchEDoubleSharp Pitch = 6
	PitchFSharp       Pitch = 6
	PitchGFlat        Pitch = 6
	PitchFDoubleSharp Pitch = 7
	PitchG            Pitch = 7
	PitchADoubleFlat  Pitch = 7
	PitchGSharp       Pitch = 8
	PitchAFlat        Pitch = 8
	PitchGDoubleSharp Pitch = 9
	PitchA            Pitch = 9
	PitchBDoubleFlat  Pitch = 9
	PitchASharp       Pitch = 10
	PitchBFlat        Pitch = 10
	PitchCDoubleFlat  Pitch = 10
	PitchADoubleSharp Pitch = 11
	PitchB            Pitch = 11
	PitchCFlat        Pitch = 11
)
const (
	PitchDiffUnisson           Pitch = 0
	PitchDiffPerfectUnisson    Pitch = 0
	PitchDiffHalfStep          Pitch = 1
	PitchDiffSemitone          Pitch = 1
	PitchDiffMinorSecond       Pitch = 1
	PitchDiffFullStep          Pitch = 2
	PitchDiffTone              Pitch = 2
	PitchDiffMajorSecond       Pitch = 2
	PitchDiffDiminishedThird   Pitch = 2
	PitchDiffAugmentedSecond   Pitch = 3
	PitchDiffMinorThird        Pitch = 3
	PitchDiffMajorThird        Pitch = 4
	PitchDiffDiminishedFourth  Pitch = 4
	PitchDiffFourth            Pitch = 5
	PitchDiffPerfectFourth     Pitch = 5
	PitchDiffAugmentedFourth   Pitch = 6
	PitchDiffDiminishedFifth   Pitch = 6
	PitchDiffFifth             Pitch = 7
	PitchDiffPerfectFifth      Pitch = 7
	PitchDiffAugmentedFifth    Pitch = 8
	PitchDiffMinorSixth        Pitch = 8
	PitchDiffMajorSixth        Pitch = 9
	PitchDiffDiminishedSeventh Pitch = 9
	PitchDiffMinorSeventh      Pitch = 10
	PitchDiffMajorSeventh      Pitch = 11
	PitchDiffOctave            Pitch = 12
	PitchDiffPerfectOctave     Pitch = 12
	PitchDiffMinorNinth        Pitch = 13
	PitchDiffMajorNinth        Pitch = 14
	PitchDiffAugmentedNinth    Pitch = 15
	PitchDiffMinorTenth        Pitch = 15
	PitchDiffMajorTenth        Pitch = 16
	PitchDiffEleventh          Pitch = 17
	PitchDiffPerfectEleventh   Pitch = 17
	PitchDiffAugmentedEleventh Pitch = 18
	PitchDiffMinorThirteenth   Pitch = 20
	PitchDiffMajorThirteenth   Pitch = 21
	PitchDiffMajorFourteenth   Pitch = 23
)

func ParseAlteration

func ParseAlteration(alt string) (Pitch, error)

func ParsePitch

func ParsePitch(input string) (Pitch, error)

func (Pitch) Add

func (p Pitch) Add(pitchDiff Pitch) Pitch

Add transposes the pitch up or down given interval (pitch difference) value.

func (Pitch) AtOctave added in v0.0.2

func (p Pitch) AtOctave(oct int8) Pitch

AtOctave transposes the pitch to given octave.

func (Pitch) GetOctave added in v0.0.2

func (p Pitch) GetOctave() int8

GetOctave returns the pitch's octave.

func (Pitch) Normalize

func (p Pitch) Normalize() Pitch

Normalize transposes the pitch to the reference [0; 11] octave.

type PitchClass added in v0.0.2

type PitchClass uint8

A PitchClass can be thought of as an "absolute" or "ideal" note; it represents all octaves of a note. In other words, it represents the class of a note's pitches.

This is the kind of construct we manipulate intuitively when we say things like: - Eb belongs to the Cm11 chord. - F# belongs to the C lydian mode.

This type is encoded as a single byte.

bit number : 7654 3 210
values:      AAAA X BBB
A: Alteration (4 bits) encoded as 8 + A where -2 (bb) <= A <= +2 (##).
B: Base Pitch Class as a number between 0 (C) and 6 (B).
X: unused

Examples:

C (natural)
PitchClass: 0b10000000 (= 128)
fields:       AAAA BBB
A: 0b1000 (= 8 + 0) -> no alteration
B: 0b000 (= 0) -> C

F#
PitchClass: 0b10010011 (= 115)
fields:       AAAA BBB
A: 0b1001 (= 8 + 1) -> 1 sharp
B: 0b011 (= 3) -> F
const (
	PitchClassC PitchClass = 0 | PitchClassNatural
	PitchClassD PitchClass = 1 | PitchClassNatural
	PitchClassE PitchClass = 2 | PitchClassNatural
	PitchClassF PitchClass = 3 | PitchClassNatural
	PitchClassG PitchClass = 4 | PitchClassNatural
	PitchClassA PitchClass = 5 | PitchClassNatural
	PitchClassB PitchClass = 6 | PitchClassNatural

	PitchClassNatural PitchClass = (8 + 0) << 4
)

func DefaultPitchClass added in v0.0.2

func DefaultPitchClass(p Pitch) PitchClass

DefaultPitchClass returns the default PitchClass associated with given pitch. On pitches that do not map to a base pitch class ("black keys"), it picks the base pitch class immediately above and flattens it.

func NewPitchClassFromChar added in v0.0.5

func NewPitchClassFromChar(char byte, alt Pitch) (PitchClass, error)

NewPitchClassFromChar creates a new PitchClass. ErrInvalidPitchClass is returned if char isn't in the range ['A','G']. ErrInvalidAlteration is returned if alt isn't in the range [-2,2].

func (PitchClass) Alt added in v0.0.2

func (p PitchClass) Alt() Pitch

Alt returns the alteration of this PitchClass.

func (PitchClass) Base added in v0.0.2

func (p PitchClass) Base() int8

Base returns the base as a number between 0 (C) and 6 (B)

func (PitchClass) BaseName added in v0.0.5

func (p PitchClass) BaseName() byte

BaseName returns the base pitch class as an ASCII byte in the ['A','G'] range.

func (PitchClass) ClipToPitch added in v0.0.5

func (p PitchClass) ClipToPitch(target Pitch) PitchClass

ClipToPitch changes p's alteration so that the target pitch belongs to the resulting PitchClass.

func (PitchClass) DoubleFlat added in v0.0.2

func (p PitchClass) DoubleFlat() PitchClass

DoubleFlat adds a double flat to the PitchClass.

func (PitchClass) DoubleSharp added in v0.0.2

func (p PitchClass) DoubleSharp() PitchClass

DoubleSharp adds a double sharp to the PitchClass.

func (PitchClass) Flat added in v0.0.2

func (p PitchClass) Flat() PitchClass

Flat adds one flat to the PitchClass.

func (PitchClass) IsEnharmonic added in v0.0.2

func (p PitchClass) IsEnharmonic(o PitchClass) bool

IsEnharmonic returns true if both PitchClasses map to the same pitches.

func (PitchClass) IsValid added in v0.0.2

func (p PitchClass) IsValid() bool

IsValid returns true if this pitch class has: - a base in the range [1,6], - an alt in the range [-2,+2].

func (PitchClass) MoveBase added in v0.0.5

func (p PitchClass) MoveBase(step int8) PitchClass

MoveBase moves the base pitch class by given (positive or negative) steps.

func (PitchClass) Pitch added in v0.0.2

func (p PitchClass) Pitch(oct int8) Pitch

Pitch returns the pitch of the PitchClass at given octave.

func (PitchClass) Pitches added in v0.0.2

func (p PitchClass) Pitches(from, to Pitch) iter.Seq[Pitch]

Pitches iterates over all pitches of this class in ascending order within the specified range.

func (PitchClass) Sharp added in v0.0.2

func (p PitchClass) Sharp() PitchClass

Sharp adds one sharp to the PitchClass.

func (PitchClass) String added in v0.0.2

func (p PitchClass) String() string

String returns a string representation of the PitchClass. "<invalid>" is returned if the PitchClass is invalid.

func (PitchClass) Transpose added in v0.0.2

func (p PitchClass) Transpose(i Interval) PitchClass

Transpose transposes a pitch class by given interval.

func (PitchClass) WithAlt added in v0.0.5

func (p PitchClass) WithAlt(alt Pitch) PitchClass

WithAlt applies given alteration to the PitchClass.

type Scale

type Scale struct {
	// Root is the root note (or the tonic) of the scale.
	Root PitchClass
	// Pattern is the scale's pattern.
	Pattern ScalePattern
}

A Scale is the association of a root Note and a ScalePattern. This pair can be extrapolated to an ordered set of notes or pitches within an octave.

This representation allows for very efficient operation on scales, such as transposing, computing modes, or testing for inclusion.

The whole structure fits within a 64bit word.

func (Scale) String

func (s Scale) String() string

String returns a string representation of the scale.

type ScalePattern

type ScalePattern uint16

A Scale pattern is represented bitwise as a 12-bit number that maps an octave. Each bit corresponds to a pitch relative to the root of the scale.

For example the major scale (e.g. C D E F G A B) has the following pattern:

pattern:  101010110101
notes:    B A G FE D C

This representation allows for very efficient set operations such as testing if a note or pitch belongs to a scale, because most of these operations can be carried out as a single native bitwise instruction on most target architectures.

const (
	ScalePatternMajor               ScalePattern = 0b101010110101 // C D E F G A B
	ScalePatternMelodicMinor        ScalePattern = 0b101010101101 // C D Eb F G A B
	ScalePatternHarmonicMinor       ScalePattern = 0b100110101101 // C D Eb F G Ab B
	ScalePatternHarmonicMajor       ScalePattern = 0b100110110101 // C D E F G Ab B
	ScalePatternDoubleHarmonicMajor ScalePattern = 0b100110110011 // C Db E F G Ab B
)

func (ScalePattern) CountNotes

func (s ScalePattern) CountNotes() int

CountNotes returns the number of notes within the ScalePattern.

func (ScalePattern) Intervals added in v0.0.2

func (s ScalePattern) Intervals() iter.Seq[Interval]

Intervals converts the scale pattern into intervals relative to the tonic. This method assumes that the scale is stepwise.

func (ScalePattern) IntervalsWithDegrees added in v0.0.5

func (s ScalePattern) IntervalsWithDegrees(degrees []int8) iter.Seq[Interval]

IntervalsWithDegrees converts the scale pattern into intervals relative to the tonic. If degrees == nil, the scale is assumed to have stepwise motion, which is suitable for most common scales in western music (heptatonic scales). Otherwise, degrees describe the absolute pitch class intervals to use.

Eg. for a major pentatonic scale (degrees are the same for minor):

// notes:          C        D    E    G    A
// intervals:      unisson, 2nd, 3rd, 5th, 6th
// degrees: []int8{1,       2,   3,   5,   6}

majorPentatonic := ScalePattern(0b1010010101)
majorPentatonic.AsIntervals([]int8{1,2,3,5,6})

func (ScalePattern) Mode

func (s ScalePattern) Mode(degree int) (ScalePattern, error)

Mode computes the n-th mode of the ScalePattern.

ErrInvalidDegree is returned if degree is not in the range [1;s.CountNotes()].

func (ScalePattern) PitchClasses added in v0.0.2

func (s ScalePattern) PitchClasses(root PitchClass) iter.Seq[PitchClass]

PitchClasses converts the scale pattern into PitchClasses starting with given root. This method assumes that the scale is stepwise.

func (ScalePattern) PitchClassesWithDegrees added in v0.0.5

func (s ScalePattern) PitchClassesWithDegrees(root PitchClass, degrees []int8) iter.Seq[PitchClass]

PitchClassesWithDegrees converts the scale pattern into PitchClasses starting with given root. degrees have the same meaning as in [ScalePattern.AsIntervals].

func (ScalePattern) Pitches added in v0.0.2

func (s ScalePattern) Pitches(root Pitch) iter.Seq[Pitch]

Pitches iterates over the pitches of the scale pattern relative to given root.

Directories

Path Synopsis
lib
abc
js
test

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL