public NoteChannelController(OrganyaPlayerContext context, OrganyaInstrument instrument, bool isDrum) { currentNoteIndex = 0; currentSampleIndex = 0; var pitchBend = instrument.FineTune - PITCH_BEND_NORMALIZATION_FACTOR; var tempNotes = new List <NoteEvent>(); foreach (var note in instrument.Notes) { var adjustedInstrumentIndex = isDrum? instrument.InstrumentIndex + 100 : instrument.InstrumentIndex; NoteEventController prov = new NoteEventController(context, note, adjustedInstrumentIndex, pitchBend, isDrum, instrument.DisableSustain); tempNotes.Add(new NoteEvent() { Note = prov, StepOffset = note.BeatNumber, LengthSteps = note.Length } ); } _notes = tempNotes.OrderBy( (ne) => ne.StepOffset ).ToList(); }
//TODO: Add reset() for looping. public float[] RequestBuffer(OrganyaPlayerContext context, long lengthSamples) { float[] sampleBuffer = new float[lengthSamples]; if (_notes.Count == 0) //Skip if there's no notes to play. { return(sampleBuffer); } float l, r; for (int i = 0; i < lengthSamples; i += 2) { //Bounds checking - make sure that we don't crash the program at the end of the song. if (this.currentNoteIndex >= _notes.Count) { break; } var activeNote = _notes[this.currentNoteIndex]; var currentBeat = Utilities.SamplesToSteps(this.currentSampleIndex, context.StepMsec, context.Config.SampleRate); //Check if the other note is overlapping this note. //When two notes overlap (common - comment this out and listen to Jenka 1), the second note should always play. //TODO: Figure out if this is necessary, or only maskign a bug. if (this.currentNoteIndex + 1 < _notes.Count && currentBeat == _notes[currentNoteIndex + 1].StepOffset) { activeNote = _notes[this.currentNoteIndex + 1]; currentNoteIndex++; } if (currentBeat > activeNote.StepOffset + activeNote.LengthSteps) { //The note is done playing. Advance to the last note unless this is the last one. currentNoteIndex++; if (currentNoteIndex < _notes.Count) { activeNote = _notes[currentNoteIndex]; } } if (currentBeat >= activeNote.StepOffset) { //We've advanced to the correct note at this point and it's active, so play it. activeNote.Note.RequestSample(out l, out r); sampleBuffer[i] = l; sampleBuffer[i + 1] = r; } else { sampleBuffer[i] = sampleBuffer[i + 1] = 0; } currentSampleIndex++; } return(sampleBuffer); }
public OrganyaSongPlayer(SampleProviderConfiguration config, OrganyaSong song, WavetableSampleLoader sampleLoader) { _context = new OrganyaPlayerContext(config, song, sampleLoader); _noteChannels = new List <NoteChannelController>(); for (int i = 0; i < song.Tracks.Count; i++) { var track = song.Tracks[i]; bool isDrum = i >= 8; var channel = new NoteChannelController(_context, track, isDrum); _noteChannels.Add(channel); } }
/// <summary> /// Initializes a new instance of the <see cref="OrgPlay.SampleController.WavetableNoteSampleProvider"/> class. /// </summary> /// <param name="context">Context.</param> /// <param name="note">Note.</param> /// <param name="bankNumber">Bank number.</param> /// <param name="pitchBendNormalized">Pitch bend normalized.</param> /// <param name="drum">If set to <c>true</c> drum.</param> /// <param name="pi">If set to <c>true</c> pi.</param> public NoteEventController(OrganyaPlayerContext context, OrganyaNote note, int bankNumber, int pitchBendNormalized, bool drum, bool pi) { _sample = context.SampleLoader.Load(bankNumber); samplePosition = 0; samplesPlayed = 0; var pitchIndex = note.Pitch % KEYS_IN_OCTAVE; _octave = note.Pitch / KEYS_IN_OCTAVE; _volumeNormalized = (float)Math.Pow(10, ((float)note.Volume / 254) - 1); _panNormalized = ((float)(note.Pan - 6)) / 6.0f; var samplePointFrequency = NOTE_POINT_FREQS[pitchIndex] + pitchBendNormalized; _normalizedSampleSpeed = samplePointFrequency / ((float)POINT_FREQ_RELATIVE_TO_SAMPLE_FREQ); if (drum) { //TODO: Not sure how drums are affected by pitch. //_normalizedSampleSpeed = 1.0f; //Do drums play at a constant point frequency? float stretchFactor = ((float)OCTAVE_REPEAT_TIMES[_octave]) / ((float)OCTAVE_ADVANCE_POINTS[_octave]); _lengthSamples = (int)(((float)_sample.Samples.Length / _normalizedSampleSpeed) * stretchFactor); } else if (pi) { //Is this correct? It should play a constant amount of samples I think. _lengthSamples = 256; //TODO: Lengths are given as periods in the notes page explanation of the 'pi' option. } else { //In this case, we know how long this'll have to be in samples. _lengthSamples = Utilities.StepsToSamples(note.Length, context.StepMsec, context.Config.SampleRate); } }