public SongStyle(SongComposer composer, Song song, MusicalScale musicalScale, float minimumNoteLength) { Composer = composer; Song = song; RunningTime = 0; MusicalScale = musicalScale; MinimumNoteLength = minimumNoteLength; ChordsTicksToHold = new int[4]; BassMelodicStyles = new BassMelodicStyle[4]; MelodicStyles = new MelodicStyle[4]; FirstDegrees = new int[4]; BassTimbres = new NoteTimbre[4]; ChordTimbres = new NoteTimbre[4]; MelodyTimbres = new NoteTimbre[4]; ChordTypes = new ChordType[4]; for (int i = 0; i < 4; i++) { ChordsTicksToHold[i] = -1; BassMelodicStyles[i] = BassMelodicStyle.None; MelodicStyles[i] = MelodicStyle.None; FirstDegrees[i] = -1; BassTimbres[i] = NoteTimbre.None; ChordTimbres[i] = NoteTimbre.None; MelodyTimbres[i] = NoteTimbre.None; ChordTypes[i] = ChordType.None; } LoadChords(); }
protected void PlayNote(int voiceNumber, float volume, MusicalNote note) { if (voiceNumber >= AudioSources.Length) { throw new ArgumentException($"You trying to play a note using voice number {voiceNumber + 1} but we only have {AudioSources.Length} dude!"); } float pitch = 1; AudioClip clip; string noteName = $"{note.Name}{note.Octave}{note.Timbre}"; if (NoteClips .ContainsKey(noteName)) { clip = NoteClips[noteName]; } else { float min = float.MaxValue; MusicalNote closest = null; int requestedIndex = MusicalScale .GetNoteAbsoluteIndex(note.Name, 8, note.Octave); foreach (var possibleNote in AvailableNotes) { int noteIndex = MusicalScale .GetNoteAbsoluteIndex(possibleNote.Name, 8, possibleNote.Octave); int difference = Mathf.Abs(noteIndex - requestedIndex); if (difference < min) { min = difference; closest = possibleNote; } } //Debug //.Log($"REQUESTED '{note.Name}{note.Octave}{note.Timbre}' ACTUAL {closest.Name}{closest.Octave}{closest.Timbre}"); clip = NoteClips[$"{closest.Name}{closest.Octave}{note.Timbre}"]; pitch = CalculateFrequencyRatio(closest, note); } var source = AudioSources[voiceNumber]; source .Stop(); source.clip = null; source.pitch = pitch; source.volume = volume; source.clip = clip; source .Play(); }
protected float CalculateFrequencyRatio(MusicalNote fromNote, MusicalNote toNote) { string noteKey = $"{fromNote.Name}{fromNote.Octave}"; if (!NoteFrequencies .ContainsKey(noteKey)) { throw new ArgumentException($"'{noteKey}' does not exist in the note frequency table"); } int fromIndex = MusicalScale .GetNoteAbsoluteIndex(fromNote.Name, 8, fromNote.Octave); int toIndex = MusicalScale .GetNoteAbsoluteIndex(toNote.Name, 8, toNote.Octave); float fromFrequency = NoteFrequencies[noteKey]; int indexDifference = fromIndex - toIndex; float toFrequency = fromFrequency * Mathf.Pow(2, indexDifference / 12f); return(toFrequency / fromFrequency); }
protected void WriteMelody() { var melodicStyle = MelodicStyles[CurrentChunkIndex]; var melodicTimbre = MelodyTimbres[CurrentChunkIndex]; int consecutiveTicksWithoutRest = 0; float melodyTime = CurrentChunkIndex * TICKS_PER_CHUNK * MinimumNoteLength; MusicalNote currentNote = null; float currentNoteEndTime = 0; MusicalChord currentChord = null; for (int tick = 0; tick < 8 * 4 * 4; tick++) { if (melodyTime < currentNoteEndTime) { melodyTime += MinimumNoteLength; continue; } // SHOULD WE REST? var restChooser = new ProbabilityChooser <bool>(); restChooser .AddItem(true, consecutiveTicksWithoutRest); restChooser .AddItem(false, 32); var shouldRest = restChooser .ChooseItem(); if (shouldRest) { consecutiveTicksWithoutRest = 0; var restLengthChooser = RestLengthChooserPerStyle[melodicStyle]; int ticksToRest = restLengthChooser .ChooseItem(); currentNoteEndTime = melodyTime + ticksToRest * MinimumNoteLength; currentNote = null; melodyTime += MinimumNoteLength; continue; } var chord = GetChordForTime(melodyTime); if (chord != null) { currentChord = chord; } var beatStrength = GetBeatStrength(melodyTime); int noteTicks = 0; bool stayOnOffBeat = StayOnOffBeatChooser[melodicStyle] .ChooseItem(); if (beatStrength == BeatStrength.Off16th && !stayOnOffBeat) { noteTicks = 1; } else if (beatStrength == BeatStrength.Off8th && !stayOnOffBeat) { noteTicks = 2; } else { var noteLengthChooser = NoteLengthChooserPerStyle[melodicStyle]; noteTicks = noteLengthChooser .ChooseItem(); } var melodyNoteInChordChooser = MelodyNoteIsInChordChoosers[beatStrength]; bool noteShouldBeInChord = melodyNoteInChordChooser .ChooseItem(); var melodyNoteInScaleChooser = MelodyNoteIsInScaleChoosers[beatStrength]; bool noteShouldBeInScale = melodyNoteInScaleChooser .ChooseItem(); int melodicInterval = MelodicIntervalChooser .ChooseItem(); int currentDegree = -1; if (currentNote != null) { currentDegree = MusicalScale .GetDegree(currentNote.Name); } int nextDegree = 0; if (noteShouldBeInChord) { if (currentNote != null) { int closestDegree = 0; int minDistance = int.MaxValue; foreach (var note in currentChord.Notes) { int ndg = MusicalScale .GetDegree(note.Name); int d = Mathf .Abs(currentDegree - ndg); if (d < minDistance) { minDistance = d; closestDegree = ndg; } } nextDegree = closestDegree; } else { var noteIndex = UnityEngine .Random .Range(0, currentChord.Notes.Length); var note = currentChord.Notes[noteIndex]; nextDegree = MusicalScale .GetDegree(note.Name); } } else { if (currentNote != null) { nextDegree = GetScaleDegree(currentDegree, melodicInterval); } else { nextDegree = GetScaleDegree(0, melodicInterval); } } int octaveChange = 0; if (melodicInterval > 7) { octaveChange++; } if (melodicInterval < -7) { octaveChange--; } if (currentNote != null) { if (melodicInterval > 0 && nextDegree < currentDegree) { octaveChange++; } if (melodicInterval < 0 && nextDegree > currentDegree) { octaveChange--; } } currentNote = MusicalScale .AscendingNotes[nextDegree]; if (currentNote.Octave - octaveChange <= 0) { octaveChange = 0; } if (currentNote.Octave + octaveChange >= 7) { octaveChange = 0; } Composer .AddNoteToSong(Song, melodyTime, MELODY_VOICE, 1f, currentNote, melodicTimbre, octaveChange); consecutiveTicksWithoutRest++; currentNoteEndTime = melodyTime + noteTicks * MinimumNoteLength; melodyTime += MinimumNoteLength; } }
protected void WriteBaseLine() { var chordTicksToHold = ChordsTicksToHold[CurrentChunkIndex]; var bassTimbre = BassTimbres[CurrentChunkIndex]; var bassmelodicStyle = BassMelodicStyles[CurrentChunkIndex]; var walkingBassLineApproachChooser = new ProbabilityChooser <int>(); walkingBassLineApproachChooser.AddItem(-2, 1); walkingBassLineApproachChooser.AddItem(-1, 1); var walkingBassLineExitChooser = new ProbabilityChooser <int>(); walkingBassLineExitChooser.AddItem(2, 1); walkingBassLineExitChooser.AddItem(1, 1); // GET THE CHORD EVENTS var chordEvents = Song.Voices[1].Events; // WRITE THE BASS LINE int octaveChange; int baseNoteTicksToHold = chordTicksToHold; switch (bassmelodicStyle) { case BassMelodicStyle.SingleNote: foreach (var chordEvent in chordEvents) { octaveChange = -1; if (chordEvent.Note.Octave <= 1) { octaveChange = 0; } // BASS IS VOICE 0 Composer .AddNoteToSong(Song, chordEvent.OccursAt, BASS_VOICE, 0.75f, chordEvent.Note, bassTimbre, octaveChange); } break; case BassMelodicStyle.AlternatingNotes: baseNoteTicksToHold = chordTicksToHold / 2; foreach (var chordEvent in chordEvents) { octaveChange = -1; if (chordEvent.Note.Octave <= 1) { octaveChange = 0; } Composer .AddNoteToSong(Song, chordEvent.OccursAt, BASS_VOICE, 0.75f, chordEvent.Note, bassTimbre, octaveChange); var otherEvent = Song.Voices[3].Events.FirstOrDefault(o => o.OccursAt == chordEvent.OccursAt); float secondNoteOccurs = chordEvent.OccursAt + baseNoteTicksToHold * MinimumNoteLength; Composer .AddNoteToSong(Song, secondNoteOccurs, BASS_VOICE, 0.75f, otherEvent.Note, bassTimbre, octaveChange); } break; case BassMelodicStyle.WalkingNotes: baseNoteTicksToHold = chordTicksToHold / 4; foreach (var chordEvent in chordEvents) { var otherEvent = Song.Voices[3].Events.FirstOrDefault(o => o.OccursAt == chordEvent.OccursAt); octaveChange = -1; if (chordEvent.Note.Octave <= 1) { octaveChange = 0; } float time = chordEvent.OccursAt; Composer .AddNoteToSong(Song, time, BASS_VOICE, 0.75f, chordEvent.Note, bassTimbre, octaveChange); time += baseNoteTicksToHold * MinimumNoteLength; int secondNoteDegree = MusicalScale .GetDegree(otherEvent.Note.Name); int nextDegree = GetScaleDegree(secondNoteDegree, walkingBassLineApproachChooser .ChooseItem()); if (nextDegree > secondNoteDegree) { octaveChange = -1; } var nextNote = MusicalScale.AscendingNotes[nextDegree]; Composer .AddNoteToSong(Song, time, BASS_VOICE, 0.75f, nextNote, bassTimbre, octaveChange); time += baseNoteTicksToHold * MinimumNoteLength; octaveChange = -1; if (otherEvent.Note.Octave <= 1) { octaveChange = 0; } Composer .AddNoteToSong(Song, time, BASS_VOICE, 0.75f, otherEvent.Note, bassTimbre, octaveChange); nextDegree = GetScaleDegree(secondNoteDegree, walkingBassLineExitChooser .ChooseItem()); if (nextDegree < secondNoteDegree) { octaveChange = -1; } nextNote = MusicalScale.AscendingNotes[nextDegree]; time += baseNoteTicksToHold * MinimumNoteLength; Composer .AddNoteToSong(Song, time, BASS_VOICE, 0.75f, nextNote, bassTimbre, octaveChange); } break; } Song .SortEvents(); }