ff-ad9833/score.fs

100 lines
3.2 KiB
Forth

\ **********************************************************************
\
\ score.fs is part of the ff-ad9833 project
\ ff-ad9833 is an audio project for FlashForth and the AD9833
\ Copyright (C) 2021 Christopher Howard
\ SPDX-License-Identifier: GPL-3.0-or-later
\ This program is free software: you can redistribute it and/or modify
\ it under the terms of the GNU General Public License as published by
\ the Free Software Foundation, either version 3 of the License, or
\ (at your option) any later version.
\ This program is distributed in the hope that it will be useful,
\ but WITHOUT ANY WARRANTY; without even the implied warranty of
\ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\ GNU General Public License for more details.
\ You should have received a copy of the GNU General Public License
\ along with this program. If not, see <https://www.gnu.org/licenses/>.
\ The purpose of this module is to provide a convenient system for
\ laying out a (single channel) musical melody in memory, and playing
\ it, using the notes.fs interface.
\ Note that, while note timing and tempo will be well within the
\ accuracy tolerances of human perception, the actual tempo will drift
\ out of sync with a precise metronome. This is for reasons described in
\ the notes module.
\ public words:
\ == nr end-score play-score
\
\ **********************************************************************
score
marker score
\ **********************************************************************
\ Internal helper words for packing and unpacking notes into the score
\ **********************************************************************
: pack-pitch ( octave u -- u ) swap #4 lshift or ;
: pack-dur ( dur-type u -- u ) swap #8 lshift or ;
: unpack-dur ( u -- dur-type u ) dup high-byte swap ;
: unpack-pitch ( u -- pitch u ) dup low-byte #4 rshift swap ;
: unpack-oct ( u -- octave ) %1111 and ;
: unpack-note ( u -- dur-type pitch octave )
unpack-dur unpack-pitch unpack-oct ;
\ **********************************************************************
\ Public words needed for laying out a score in memory. Each note should
\ be laid out in this form:
\ dur-type pitch octave ==
\ The dur-type, pitch, and octave constants are taken from the note
\ module. The `==' packs the note into a single 16-bit cell and then
\ stores it in the current memory section with the `,' word.
\ To use a rest note, use the form
\ dur-type NR NR ==
\ where NR is the `nr' constant.
\ At the end of your score, instead of inputting a note, use the
\ `end-score' word to store a end-of-score marker in memory. And example
\ score is given below.
\ Play a score by passing the memory address of your score to the
\ `play-score' word.
\ The score format is one 16-bit cell per note:
\ bits 0-3: octave
\ bits 4-7: pitch
\ bits 8-15: dur-type
\ The end-of-score marker a 16-bit cell filled with $ffff.
\ **********************************************************************
: == ( dur-type pitch octave -- ) pack-pitch pack-dur , ;
%1110 constant nr
: end-score $ffff , ;
: play-score ( a -- )
begin dup @ dup $ffff <> while
unpack-note dup %1111 and %1110 <> if
play-note else
drop drop rest then
cell + repeat drop drop ;