gnu-emacs-sources
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

beep.el v. 0.0


From: D Goel
Subject: beep.el v. 0.0
Date: Fri, 29 Jul 2005 12:46:27 -0400
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

 beep.el --- music sheet player, synthesizer, music via bash scripts


-----------------------------------------------------

INTRODUCTION:
============
beep.el is an interface between the music sheet spec and a
corresponding beep player.  It can help you synthesize music,
converting your keystrokes a,bq,c#, etc. to frequency+duration as you
type them, and optionally playing them through your pc-speaker.  Or
play music from such text-based music files.  Or just have fun with
the current buffer and see M-x beep-play-buffer what it sounds like.
It can generate a bash script for the current music sheet, the script
uses beep to play the music, and such scripts can be useful inside
more general scripts like crontab reminders.  There is currently no
interface provided to output the music through regular sound speakers.

To listen to some demos, make sure you have a pc-speaker, apt-get
install beep, and then type M-x beep-demo-<name>.

Next, fiddle with user options like M-x beep-options-octave-increase
or M-x beep-options-speed-decrease, and replay the demo.  As you can
see, beep.el also generates the equivalent bash scripts for your use
for things like crontab reminders.  Don't forget to reset options
using M-x beep-options-reset.

Next, type M-x beep-synthesizer (or M-x beep-mode anywhere else), to
also play whatever you type.  Then, type something like cv4 (v4 sets
the velocity or the speed) followed by a space. Next, type d SPC, e
SPC f SPC g SPC a SPC, b SPC and finally co4 SPC (o4 takes us to the
4th octave).  Type SPC again at any time to repeat the last sound. At
any time, type M-x beep-eval-buffer, or C-c C-c.  



Type M-x beep-commentary for much more.

In the next post, also find a beep.sh to call beep.el from bash
script, though you should probably hardly ever need to do so, since
beep.el generates direct bash script for the song for you.

-----------------------------------------------------
The latest version can be had from
http://gnufans.net/~deego/emacspub/lisp-mine/beep/ .
;;;---------------- CUT HERE -------------------------------

;;; beep.el --- music sheet player, synthesizer, music via bash scripts
;; Time-stamp: <2005-07-29 12:45:48 deego>
;; Copyright (C) 2005 D. Goel
;; Emacs Lisp Archive entry
;; Filename: beep.el
;; Package: beep
;; Author: D. Goel <address@hidden>
;; Keywords: crontab, bash music, sheet music player, synthesizer
;; Version:  0.0
;; URL: http://gnufans.net/~deego/emacspub/lisp-mine/beep/
;; For latest version:

(defconst beep-home-page
  "http://gnufans.net/~deego/emacspub/lisp-mine/beep/";)


 
;; This file is NOT (yet) part of GNU Emacs.
 
;; This 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 2, or (at your option)
;; any later version.
 
;; This 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 GNU Emacs; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;; 
;; See also:


;; Quick start:
(defconst beep-quick-start
  "Type M-x beep-introduction
"
)

(defun beep-quick-start ()
  "Provides electric help from variable `beep-quick-start'."
  (interactive)
  (with-electric-help
   '(lambda () (insert beep-quick-start) nil) "*doc*"))

;;; Introduction:
;; Stuff that gets posted to gnu.emacs.sources
;; as introduction
(defconst beep-introduction
  "beep.el is an interface between the music sheet spec and a
corresponding beep player.  It can help you synthesize music,
converting your keystrokes a,bq,c#, etc. to frequency+duration as you
type them, and optionally playing them through your pc-speaker.  Or
play music from such text-based music files.  Or just have fun with
the current buffer and see M-x beep-play-buffer what it sounds like.
It can generate a bash script for the current music sheet, the script
uses beep to play the music, and such scripts can be useful inside
more general scripts like crontab reminders.  There is currently no
interface provided to output the music through regular sound speakers.

To listen to some demos, make sure you have a pc-speaker, apt-get
install beep, and then type M-x beep-demo-<name>.

Next, fiddle with user options like M-x beep-options-octave-increase
or M-x beep-options-speed-decrease, and replay the demo.  As you can
see, beep.el also generates the equivalent bash scripts for your use
for things like crontab reminders.  Don't forget to reset options
using M-x beep-options-reset.

Next, type M-x beep-synthesizer (or M-x beep-mode anywhere else), to
also play whatever you type.  Then, type something like cv4 (v4 sets
the velocity or the speed) followed by a space. Next, type d SPC, e
SPC f SPC g SPC a SPC, b SPC and finally co4 SPC (o4 takes us to the
4th octave).  Type SPC again at any time to repeat the last sound. At
any time, type M-x beep-eval-buffer, or C-c C-c.  



Type M-x beep-commentary for much more.

In the next post, also find a beep.sh to call beep.el from bash
script, though you should probably hardly ever need to do so, since
beep.el generates direct bash script for the song for you.
" )

;;;###autoload
(defun beep-introduction ()
  "Provides electric help from variable `beep-introduction'."
  (interactive)
  (with-electric-help
   '(lambda () (insert beep-introduction) nil) "*doc*"))

;;; Commentary:
(defconst beep-commentary
  "First, see M-x beep-introduction.



* As you have learnt, every /INSTRUCTION/ is a series of characters
  with no spaces in them, for example, \"ao4\" is an instruction.

* A beep file or a SENTENCE is a series of instructions separated by
  whitespace, for example, the sentence \"v3 ao4 f\" comprises 3
  instructions.   (The current string or file or buffer being played is
  called a /SONG/). 


* The most important (though optional) part of an instruction is the
  NOTE.  Letters c,d,e,f,g,a,b comprise the note (for any octave).
  The capital versions, C, D, E .. comprise the corresponding sharp
  versions.

* The letter z is treated as a note, except that it means: silence.


* Next, we discuss modifiers.  These modifiers may appear by
  themselves in an instruction, or they may be combined with other
  modifiers and possibly a note.

* v==> velocity, or speed. Use v to set the speed. Thus, v2 is double
  the speed of v1, which is double of v0, and so on.  In other words,
  v sets the duration for which each note is played.

* o==> octave, or tempo.  Use o to set the octave.  Thus, o3 is one
  octave to the right of o2 (which means twice the frequency).


* Thus, the following sentences are equivalent:

  \"v3 c v4 a b\" \"v3c av4 b\" == \"cv3 av4 bv4\" == \"cv3 v4a v4 v4 v4
  v4b\", you get the idea.   For each instruction, the modifiers are
  processed, and then the note, if present in the instruction, is played.





That is all you really need to know to compose your own music in
beep.el, type M-x beep-synthesizer and enjoy.  The above is the usage
we recommend.  Next, we discuss various other smaller features and
compatibility hacks provided, and other issues:

* Any and all \"junk\" in an instruction is simply ignored.  We simply
  play the part of an instruction that we can parse.

* Sharps can also be specified by using \"#\" somewhere in the
  instruction.  Thus, a# == A == #a == ##a.  We tolerate this form,
  but prefer using capital versions for sharp.

* What about flats?  Pitchwise, d flat is nothing but c sharp, so you
  might as well use C.  However, you can also specify d flat as db, in
  this case, the note must be the very first part of the
  instruction. Thus, dbo4 == Co4.

  To repeat, the flat note must be the very first part of the note.
  Thus, dbo3 is ok, but your b modifier will be ignored if you typed
  o3db.



* All lines beginning with # are ignored.  To repeat, only lines
  BEGINNING with # are ignored.


* <num>: A number where it is not otherwise expected is often treated
         as o<num>.  Thus, a4 is same as ao4.  This instruction sets
         the octave to 4 and then plays a in that octave.

         We currently tolerate this format only when present in
         original ditty-like instructions where you don't use our
         special u, U, p, P commands.
         
         Thus, most often, you might want to use things like a4, i.e.,
         a letter followed by a number, though we recommend using ao4
         instead.



 Vn: V followed by a number sets the velocity, or the speed at which
     we play the current note.  The larger the V, the smaller duration
     the current note will have.  Specifically, the duration is
     directly proportional to (2^(-V)). The number can also be a float
     and/or negative. Future notes are not affected.

 v: which we introduced already, is just like V, except that it
    changes the default duration for future notes (only in the
    *current song*) as well.  Thus, your very first instruction might
    simply be v3 (to set the speed for the whole melody).  Or it might
    be qe (see later, which is equivalent to tXX).


 u: Can be used in the middle of a song to reset the current speed
    to the one at the beginning of the song.
 
 U: Sets the speed to the system default speed.  You should avoid
    using U in your songs, so that you allow the user to specify such
    things as a default speed when playing your song.  In fact, we
    currently bother to not even implement this.

 O<num>: O followed by a number specifies seta the octave or the tempo
         (or the frequency multiplier) to use when playing the current
         note, if any.  The typical octave values are 1 to 5. The
         default octave is 3.  The number can also be a float and/or
         negative.

    
 o: , which we already introduced, is just like O, except that it
    changes the default octave for futures notes in the current song
    as well.  well. This is like ditty's default.
 
 p: Can be used somewhere in the middle of a song to reset the current
    default octave to the one at the beginning of the song.

 P: Resets the default octave to the system default octave.  You
    should avoid using P in your songs, so that the user can specify
    such things as default pitch when playing your song.  In fact, we
    currently bother to not even implement this.
    


* Usually, the case convention is chosen so that the smallcase is the
  one you will use most frequently.

These keys are defined for ditty compatibility:

 w: whole note. Is same as v0.
   
 h: half note.  Is same as v1.

 q: quarter note.  Same as v2.

 e: eigth note.  Same as v3, whose usage is strongly advised
    instead. This e has a potential for confusion with the letter e.
    Thus, your e in an instruction is parsed as an eigth note only if
    the note had another letter (which may itself be an e) preceding
    the e.

 s: sixteenth note.  Same as v4.
 t:  thirty-second note.  Same as v5.

 
  ,: separator, you will probably never use it.  This can help you
     separate two numbers.  Thus v3,2 getd parsed as v3o2 instead of
     v32.


* (ditty compliance) multiple t specifications within the same note
  are additive.  Thus, for example, ahq means play the note a for a
  speed of (half + quarter).  That is same as saying av1v2.


*  Any other \"junk\" in an instruction is silently ignored.
 
* Thus, an example of a valid instruction is e4q, which asks us to
  play e, make the current and the default octave to be the 4th octave
  places, and increase the current and set the default speed to 4. (q
  stands for quarter, which is same as v2).


* beep's instruction set is a superset of that of ditty.  Sending
  outputs to pc speakers from a terminal does not work from ditty
  because of tty issues. beep.el, however, does not suffer from this
  issue, since, by default, we simply pipe any instructions to a
  typical system's built-in 'beep -f...'  If you do not have beep
  installed, apt-get install beep.



* We don't know yet how to send a freq/duration code to the sound
  card, but if you know how to send it, simply code it in a
  beep-string-to-program-my, and setq beep-string-to-program-function
  to beep-string-to-program-my.


* By default, we use the pc's built-in speaker rather than the sound
  cards, so that beep should be useful for such things as playing
  appropriate warnings from crontab.


* You can also call this file from shell-scripts via a ditty-like
  interface.  See the file beep.sh, whose interface and options are
  very similar to that of ditty.  beep.sh options: :f for filename
  beep.sh options: :t for speed or duration.  Each note is played for
  that long.


* Lisp: M-x beep-play-string is the wrapper which calls the various
  processors on a string.
 
  Processing: Each beep instruction is converted to a beep lisp form,
  something like '((note \"a\") (octave 2)) from where it is then
  converted to a series of (frequency,duration) specifiers, from which
  it gets concveted to shell-commands of the form 'beep -f ...'.  All
  that is finally converted into an executable beep lisp form
  (shell-command \"beep -f ...\").


 

* You can also use beep to see how to convert strings to their bash
  beep command equivalents. These equivalents can then be used in, or
  made into bash scripts.  The *beep-log* stores these every time you
  play some string via beep.

* For some sick fun, take a random buffer and play it to see how it
  sounds.  Then, take its bash output and beep it again, and so on.


* It is most instructive to see how the source code of M-x
  beep-demo-updown works.  The source code is explained there.  See
  also beep-demo-updown2.

NOTHING IN THIS PROGRAM IS RESTRICTED TO INTEGERS or to positive
numbers.  FEEL FREE TO USE FLOATS FOR *ANY* ARGUEMNTS FOR FINER
EFFECTS, including octave, velocity, etc.


To customize, customize all variables defined with a defcustom, but
leave the defvars alone.

"
)

(defun beep-commentary ()
  "Provides electric help from variable `beep-commentary'."
  (interactive)
  (with-electric-help
   '(lambda () (insert beep-commentary) nil) "*doc*"))

;;; History:

;;; Bugs:

;;; New features:
(defconst beep-new-features
  ""
)

(defun beep-new-features ()
  "Provides electric help from variable `beep-new-features'."
  (interactive)
  (with-electric-help
   '(lambda () (insert beep-new-features) nil) "*doc*"))

;;; TO DO:
(defconst beep-todo
  "Help..."
)

(defun beep-todo ()
  "Provides electric help from variable `beep-todo'."
  (interactive)
  (with-electric-help
   '(lambda () (insert beep-todo) nil) "*doc*"))

(defconst beep-version "0.0")
(defun beep-version (&optional arg)
   "Display beep's version string.
With prefix ARG, insert version string into current buffer at point."
  (interactive "P")
  (if arg
      (insert (message "beep version %s" beep-version))
    (message "beep version %s" beep-version)))

;;==========================================
;;; Requires:
(eval-when-compile (require 'cl))

;;; Code:

(defgroup beep nil
  "The group beep."
  :group 'applications)
(defcustom beep-before-load-hook nil
  "Hook to run before loading beep."
  :group 'beep)
(defcustom beep-after-load-hook nil
  "Hook to run after loading beep."
  :group 'beep)
(run-hooks 'beep-before-load-hook)

(defcustom beep-verbosity 100
  "How verbose to be.
Once you are experienced with this lib, 0 is the recommended
value.  Values between -90 to +90 are recommended for general use and
the rest for debugging."
  :type 'integer
  :group 'beep)
(defcustom beep-interactivity 0
  "How interactive to be.
Once you are experienced with this lib, 0 is the recommended
value.  Values between -90 and +90 are recommended for general use and
the rest for debugging."
  :type 'integer
  :group 'beep)
(defcustom beep-y-or-n-p-function 'beep-y-or-n-p
  "Function to use for interactivity-dependent  `y-or-n-p'.
Format same as that of `beep-y-or-n-p'."
  :type 'function
  :group 'beep)
(defcustom beep-n-or-y-p-function 'beep-n-or-y-p
  "Function to use for interactivity-dependent `n-or-y-p'.
Format same as that of `beep-n-or-y-p'."
  :type 'function
  :group 'beep)
(defun beep-message (points &rest args)
  "Signal message, depending on POINTS and `beep-verbosity'.
ARGS are passed to `message'."
  (unless (minusp (+ points beep-verbosity))
    (apply #'message args)))
(defun beep-y-or-n-p (add prompt)
  "Query or assume t, based on `beep-interactivity'.
ADD is added to `beep-interactivity' to decide whether
to query using PROMPT, or just return t."
  (if (minusp (+ add beep-interactivity))
        t
      (funcall 'y-or-n-p prompt)))
(defun beep-n-or-y-p (add prompt)
  "Query or assume t, based on `beep-interactivity'.
ADD is added to `beep-interactivity' to decide whether
to query using PROMPT, or just return t."
  (if (minusp (+ add beep-interactivity))
        nil
      (funcall 'y-or-n-p prompt)))

;;; Real Code:


(defun beep-remove-comments (str)
  "Remove comments from a string."
  (let* ((strlines
          (split-string str "\n"))
         (strlines2
          (remove-if
           #'(lambda (s)
               (string-match "^#" s))
           strlines)))
    (mapconcat 'identity strlines2 "\n")))

(defvar beep-log-current-string nil)
(defvar beep-log-current-stringlist  nil)


(defun beep-check-maybe ()
  (when (> beep-verbosity 30)
    
    (let ((beepp (> (length (shell-command-to-string "which beep")) 3)))
      (unless beepp
        (ding t)
        (message "You do not seem to have beep installed!")
        (sit-for 1)
        (ding t)
        (message "Please apt-get install beep first. ")
        (sit-for 1)))))


(defun beep-play-string (str)
  (interactive "s")
  (require 'cl)
  (beep-check-maybe)
  (beep-log-clear)

  (let*
      (
       (beep-log-current-string str)       
       (program
        (funcall beep-string-to-program-function 
                 str)))
    (when (> beep-verbosity 60)
      (set-buffer "*beep-log*")
      (goto-char (point-min))
      (display-buffer "*beep-log*")
      (sit-for 0)
      )
    (eval program)))


(defun beep-string-to-program-default (str)
  (beep-shell-commands->program
   (beep-freqduration->shell-commands
    (beep-lisp->freqduration
     (beep-notes->lisp str)))))

(defcustom beep-string-to-program-function 
  'beep-string-to-program-default 
  "" :group 'beep)


(defun beep-freqduration->shell-commands (fd)
  "Negative freq, means no-op."
  (let ((shells
         (mapcar
          (lambda (arg)
            (let ((f (car arg))
                  (d (cadr arg)))
              (if (or (< f 0) (< d 0))
                  ;; either use this or use format %s..
                  (copy-sequence "true;")
                (when (< f 0.01)
                  (setq f 0.01))
                (when (> f 19999)
                  (setq f 19999))
                (format "beep -f %s -l %s;"
                        f d))))
          fd)))
    (beep-log-as-bash-script
     ;;(mapconcat 'identity shells "\n")
     (mapconcat
      'identity
      (mapcar* (lambda (a b) (concat a " # " b))
               shells beep-log-current-stringlist)
      "\n"))
    shells))


  



(defun beep-log-as-bash-script (bashcommands)
  (beep-log
   (concat "#!/bin/bash"
           "\n"
           (if beep-bash-name
               (format "## %s\n"  beep-bash-name)
             "")
           "## This bash script was generated by beep.el on "
           (format-time-string "%Y-%m-%d T%T%z\n")
          (if (stringp beep-log-current-string)
              (concat
               "## from the following notes: \n"
               (replace-regexp-in-string
                "^" "## "
                
                (beep-utils-singlify-newlines
                 beep-log-current-string)))
            "")
          "\n"
          bashcommands "\n\n\n")
   t))


(defun beep-utils-singlify-newlines (str)
  (with-temp-buffer
    (insert str)
    (while
        (progn
          (goto-char (point-min))
          (search-forward "\n\n" nil t))
      (replace-match "\n"))
    (buffer-substring-no-properties (point-min) (point-max))))





(defun beep-log (arg &optional suppressp)
  (save-excursion
    (set-buffer (get-buffer-create "*beep-log*"))
    (ignore-errors (shell-script-mode))
    (goto-char (point-max))
    (unless suppressp
      (insert "\n\n__________________________________________________________")
      (insert (format-time-string "\n%Y-%m-%d T%T%z"))
      (insert "\n"))
    (if (stringp arg)
      (insert arg)
      (insert (format "%S" arg)))))


(defun beep-log-clear ()
  (save-excursion
    (set-buffer (get-buffer-create "*beep-log*"))
    (delete-region (point-min) (point-max))))


(defcustom beep-shell-buffer "*beep-shell-commands*" ""
:group 'beep
)

(defun beep-shell-commands->program (cmds)
  (cons
   'progn
   (mapcar
    (lambda (arg)
      `(shell-command ,arg ,beep-shell-buffer))
    cmds)))






(defun beep-debug-string->freqduration (str)
  (beep-lisp->freqduration
   (beep-notes->lisp str)))







(defun beep-notes->lisp (str)
  (let* (
         ;;(strlines
         ;;(split-string str "\n"))
         ;;(strlines2
         ;;(beep-remove-comments str))
         ;;(remove-if
         ;;(lambda (str) (string-match "^#" str))
         ;;strlines))
         (str2 (beep-remove-comments str))
         (strs (remove "" (split-string str2 "[ \t\n\r\v]+"))))
    (beep-notes->lisp-from-stringlist
     strs)))

(defun beep-play-file (file)
  (interactive "f")
  (with-temp-buffer
    (insert-file-contents file)
    (beep-play-buffer)))

(defun beep-play-buffer ()
  (interactive)
  (beep-play-string
   (buffer-substring-no-properties (point-min) (point-max))))

(defun beep-play-region  (a b)
  (interactive "r")
  (beep-play-string
   (buffer-substring-no-properties a b)))



(defcustom beep-bash-name nil
  "When set to a string, adds that comment to the bash script."
  :group 'beep
  )

(defun beep-notes->lisp-from-stringlist (commands)
  (setq beep-log-current-stringlist commands)
  (mapcar 'beep-note-to-lisp-ignore-errors commands))

(defconst beep-duration 2000.00
  "change speed instead, no need to change
 this var. ")
;;====================================================

(defcustom beep-param-speed-default 0 ""
:group 'beep
)
(defcustom beep-param-speed-user 0 "" :group 'beep)
(defcustom beep-param-speed beep-param-speed-default
  "Current param" :group 'beep
  )
(defcustom beep-param-speed-song beep-param-speed-default
  "starting param at the beginning of a song."
  :group 'beep
  )

;;;====================================================

(defcustom  beep-param-octave-default 3 ""
:group 'beep)
(defcustom beep-param-octave-user 0
  "How much the user wants the frequency displaced to the right."
  :group 'beep
  )
(defcustom beep-param-octave beep-param-octave-default
  "Current param."
  :group 'beep  )
(defcustom beep-param-octave-song beep-param-octave-default
  "Starting param at the beginning of a song."
  
  :group 'beep  )

;;;====================================================


(defun beep-options-speed-increase (&optional num)

  (interactive "p")
  (setq beep-param-speed-user
        (+ num beep-param-speed-user))
  (message "User Speed set to %s" beep-param-speed-song))



(defun beep-options-speed-decrease (&optional num)

  (interactive "p")
  (beep-options-speed-increase (- num)))

(defun beep-options-octave-increase (num)
  (interactive "p")
  (setq beep-param-octave-user
        (+ num beep-param-octave-user)))
(defalias 'beep-options-tempo-increase 'beep-options-octave-increase)

(defun beep-options-octave-decrease (num)
  (interactive "p")
  (beep-options-octave-increase (- num)))


(defalias 'beep-options-tempo-decrease 'beep-options-octave-decrease)

(defun beep-options-reset ()
  (interactive)
  (setq beep-param-octave-user 0)
  (setq beep-param-speed-user 0))


(defun beep-reset ()
  "No need to ever use this."
  (beep-reset-octave)
  (beep-reset-speed))



(defun beep-reset-octave ()
  "No need to ever use this."
  (interactive)
  (beep-reset-octave-song)
  (setq beep-param-octave beep-param-octave-default)
  )

(defun beep-reset-octave-song ()
  (setq beep-param-octave beep-param-octave-song))



(defun beep-reset-speed ()
  "No need to ever use this."
  (interactive)
  (beep-reset-speed-song)
  (setq beep-param-speed beep-param-speed-default)
  )

(defun beep-reset-speed-song ()
  (setq beep-param-speed beep-param-speed-song))





(defun beep-note-to-lisp-ignore-errors (&rest args)
  (ignore-errors
    (apply 'beep-note-to-lisp args)))

(defun beep-note-to-lisp (note)
  (assert (stringp note))
  ;; convert any but the first e's into v3's.
  (while
      (string-match "^.*[a-gA-G].*\\(e\\)" note)
    (setq note (replace-match "v3" nil nil note 1)))
  ;; convert all flats to regulars or sharps:
  (cond

   ((string-match "^cb" note)
    (setq note (replace-match "c" nil nil note)))

   ((string-match "^db" note)
    (setq note (replace-match "C" nil nil note)))
   ((string-match "^eb" note)
    (setq note (replace-match "D" nil nil note)))

   ((string-match "^fb" note)
    (setq note (replace-match "f" nil nil note)))

   ((string-match "^gb" note)
    (setq note (replace-match "F" nil nil note)))

   ((string-match "^ab" note)
    (setq note (replace-match "G" nil nil note)))

   ((string-match "^bb" note)
    (setq note (replace-match "A" nil nil note))))

   


  (let*
      (
       (sharpp (string-match "#" note))
       (subnotes (beep-note-to-lisp-break-note note))
       (subnoteslisp1
        (apply
         'append
         (mapcar
          (if sharpp 'beep-subnote-to-lisp-sharped 'beep-subnote-to-lisp )
          subnotes))))
    (beep-lisp-note-cleanup subnoteslisp1)))

(defun beep-lisp-note-cleanup (note)
  "Attempt to clean up a lispy note... Removing, for example, multiple letter
specs. If there are more than 1, remove all but the first."
  (let ((aa (member* 'letter note :key 'car)))
    (if
        aa
        (cons aa (remove* aa note :test #'equal)))
    note))





(defun beep-note-to-lisp-break-note (note)
  "Break note into subnotes..."
  (let* ((notenewline (replace-regexp-in-string "[a-zA-Z]" "\n\\&"
                                                note)))
    (remove "" (split-string notenewline "\n"))))

(defvar beep-subnote-to-lisp-sharpp nil)
(defun beep-subnote-to-lisp-sharped (subnote)
  (let ((beep-subnote-to-lisp-sharpp t))
    (beep-subnote-to-lisp subnote)))

(defun beep-subnote-to-lisp (subnote)
  "Returns a list of lisp expression(s) resulting from a subnote.  The
  list should typically be of length 1, but sometimes it is nil, and
  sometimes its length is 2"
  (let (bc1 bcreststr bcrest (restnum 0)
            (len (length subnote))
            (bcs (string-to-list subnote))
            (num (string-to-number subnote)))
    (if
        (= len 0) nil
      (progn
        (setq bc1 (first bcs) bcrest (rest bcs))
        (setq bcreststr (if bcrest (apply 'string bcrest) ""))
        (if bcreststr (setq restnum (string-to-number bcreststr)))
      
      (cond
       ((member bc1 '(?a ?b ?c ?d ?e ?f ?g ?A ?B ?C ?D ?E ?F ?G ?z ?Z))
        (append (list
                 (list
                  'beep-command-letter
                  (intern
                   (string
                    (if beep-subnote-to-lisp-sharpp
                        (upcase bc1)
                      bc1)))))
                ;; The rest of the string may be a number.. in which
                ;; case it should be parsed.
                (beep-subnote-to-lisp bcreststr)))
       ((member bc1 '(?o))
        (list (list 'beep-command-o restnum)))
       ((member bc1 '(?O))
        (list (list 'beep-command-O restnum)))
       ((member bc1 '(?v))
        (list (list 'beep-command-v restnum)))
       ((member bc1 '(?V))
        (list (list 'beep-command-V restnum)))
       ;; add sharps to the command.. even though sharpening should have already
       ;; been taken care of..
       ((member bc1 '(?#))
        (append (list (list 'beep-command-sharp))
                (beep-subnote-to-lisp bcreststr)))


       ((member bc1 '(?w))
        (list (list 'beep-command-v 0)))

       ((member bc1 '(?h))
        (list (list 'beep-command-v 1)))

       ((member bc1 '(?q))
        (list (list 'beep-command-v 2)))

       ;; ?e, eigths should already have been processed.
       ((member bc1 '(?s))
        (list (list 'beep-command-v 4)))


 
       ((member bc1 '(?t))
        (list (list 'beep-command-v 5)))

      
       ((member bc1 '(?u))
        (list (list 'beep-command-u)))


       ((member bc1 '(?U))
        (append (list (list 'beep-command-U))))


       ((member bc1 '(?p))
        (append (list (list 'beep-command-p))))

       ((member bc1 '(?P))
        (append (list (list 'beep-command-P))))
       ((and
         (member bc1 '(?1 ?2 ?3 ?4 ?5))
         (member num '(1 2 3 4 5)))
        (list (list 'beep-command-o num)))
       (t nil))
      ))))



(defun beep-song-defaults ()
  (setq beep-param-speed beep-param-speed-song)
  (setq beep-param-octave beep-param-octave-default))

(defun beep-lisp->freqduration (notes)
  "The input notes are lispy notes."
  ;; first set current values to defaults, if not already the case.
  (beep-song-defaults)
  ;; Next, for each lisp command, find the appropriate frequency and duration.
  (mapcar 'beep-lisp->freqduration-once notes))

(defvar beep-working-freq nil)
(defvar beep-working-duration nil)
(defvar beep-working-speed nil)
(defvar beep-working-speed-save-p nil)
(defvar beep-working-speed-specified-p nil)
(defvar beep-working-speed-working nil)
(defvar beep-working-octave nil)
(defvar beep-working-found-letter-p nil)

(defun beep-lisp->freqduration-once (note)
  (if note
      (let ((beep-working-freq 1)
            (beep-working-octave beep-param-octave)
            (beep-working-speed beep-param-speed)
            (beep-working-found-letter-p nil)
            (beep-working-duration beep-duration)

            (beep-working-speed-save-p nil)
            (beep-working-speed-specified-p nil)
            (beep-working-speed-working nil)
            )
        (beep-lisp->freqduration-once-process note)
        (when beep-working-speed-specified-p

          (setq beep-working-speed
                (beep-working-speed-from-speed-list
                 beep-working-speed-working))
          (when beep-working-speed-save-p
            (setq beep-param-speed beep-working-speed)))
     
        (if beep-working-found-letter-p
            (list
             ;; the current freq already corresponds to an octave of 3 by
             ;; default, so remove 3 before changing it any more,..
             (* beep-working-freq
                (expt 2.0 (+ beep-working-octave
                             beep-param-octave-user
                             -3)))
             (/ (float beep-working-duration)
                (expt 2.0
                      (+ beep-working-speed
                         beep-param-speed-user))))
          (list -1 0)))
    (list -1 0)))


(defun beep-log2 (arg)
  (/ (log arg) (log 2))
  )



(defun beep-working-speed-from-speed-list (tlist)
  "Want to make speeds additive.  So that if a user specifies a speed
of hq (1,2) which means half+quarter, the result comes out to what we
expect. "
  (beep-log2
   (/ 1.0
      (apply '+
             (mapcar  (lambda (arg)
                        (expt 2.0 (- arg)))
                      tlist)))))

  
(defun beep-lisp->freqduration-once-process (note)
  (mapc 'beep-lisp->freqduration-once-process-subnote note))

(defun beep-lisp->freqduration-once-process-subnote (subnotes)
  ;; we should never have to see things like dflat, etc, here, since
  ;; we should have already converted those.  But, if the user
  ;; directly likes to specify lisp, we might as well process things
  ;; like 'dflat again..

  (let ((sym (car subnotes )) (arg (cadr subnotes)))
  (case sym
    (beep-command-letter
     (setq beep-working-found-letter-p t)
     (setq beep-working-freq
           (case arg
             ;;(?a         220.0)
             ;;(?A        233.1)

             ;; See
             ;; 
http://www.physics.mcgill.ca/~guymoore/ph224/different_scales.html
             ;; We use the tempered scale here.


             ;; NO CFLAT so make it same as c
             (cflat        261.55) ;; 1.05946



             (c         261.55) ;; the base frequency 1

             ;; sharp   2187/2048
             ;;(C        277.2) ;; 
             (C        277.101763000000) ;; 1.05946
             (dflat        277.101763000000) ;; 1.05946
             ;;; (dflat       277.2) ;; d flat == c sharp.



             ;;(d         293.7)
             (d         293.579413000000) ;; 1.12246
             ;; (D        311.1)
             (D        311.035260000000) ;; 1.18920
             (eflat        311.035260000000) ;; same as D#

             

             ;;(e         329.6)
             (e         329.532076000000) ;; 1.25992
             ;;there is NO E SHARP, so make it same
             ;; as e
             (E         329.532076000000)

             


             ;; NO F FLAT so make it same as f
             (fflat    349.124786500000) ;; 1.33483
             (f    349.124786500000) ;; 1.33483
             (F       369.886625500000) ;; 1.41421
             (gflat       369.886625500000) ;; gflat==gsharp



             (g         391.880365000000)               ;1.49830
             ;;(G        415.3)
             (G        415.184470000000) ;; 1.58740
             (aflat        415.184470000000) ;; same as gsharp



             (a         439.872174500000) ;; 1.68179
             (A        466.027174500000)  ;; 1.78179
             (bflat        466.027174500000)  ;; ==asharp

             (b         493.738397000000) ;; 1.88774
             (B         493.738397000000) ;; NO BSHARP!!

             (z 0.0001); pause
             (Z 0.0001); pause
             ;;C         523.2
             (otherwise 1))))
    (beep-command-O
     (setq beep-working-octave arg))

    (beep-command-o
     (setq beep-working-octave arg)
     (setq beep-param-octave arg))

    (beep-command-V
     (setq beep-working-speed-specified-p t)
     (add-to-list 'beep-working-speed-working arg))

    (beep-command-v
     (setq beep-working-speed-specified-p t)
     (add-to-list 'beep-working-speed-working arg)
     (setq beep-working-speed-save-p t))
     
     ;;(setq beep-param-speed arg))
       


    (beep-command-u
     (setq beep-working-speed beep-param-speed-song)
     (setq beep-param-speed beep-param-speed-song))
       


    (beep-command-p
     (setq beep-working-octave beep-param-octave-song)
     (setq beep-param-octave beep-param-octave-song))
    (otherwise nil))))


    
                                      


(defun beep-work-frequency (freq)
  (beep-debug freq)
  (shell-command
   (format "beep -f %d -l %d" freq beep-duration)))

(defvar beep-debug-p nil)
(defun beep-debug  (&rest args)
  (message "DEBUG: %S" args))

;;;====================================================

(defun beep-reverse-string (str)
  "Reverse the order of the notes in a string. "
  (let* ((str2 (beep-remove-comments str))
         (strs (split-string str2))
         (strsr (reverse strs)))
    (mapconcat 'identity strsr " ")))

         

;;;###autoload
(defun beep-demo ()
  "The files for furelise , notes.dit and o_susanna.dit are copied
from ditty's examples/ directory.  The author has released all rights
to these files.

Accompanying files named *.dit are files you may input into ditty.
These files are, as an exception, exempted from the LICENSE - I
release all rights to these, and they may be distributed as being in
the public domain.  Distributions of this software need not include
these files - they are meant only for example.

"
  (interactive)
  (message "Please type M-x beep-demo-<name> instead."))

(defun beep-demo-furelise ()
  (interactive)
  (let ((beep-verbosity 100)
        (beep-bash-name "Fur Elise"))
    (beep-play-string
   "
e4e d# e d# e b3 d4 c a3qe ce e a bqe ee g# b c4qe e3e
e4e d# e d# e b3 d4 c a3qe ce e a bqe ee c4 b3 aqe be c4 d
eqe g3e f4e e dqe f3e e4 d cqe e3e d4 c b3h
e4e d# e d# e b3 d4 c a3qe ce e a bqe ee g# b c4qe e3e
e4e d# e d# e b3 d4 c a3qe ce e a bqe ee c4 b3 aqe")))



(defun beep-demo-notes ()
  (interactive)
  (let ((beep-verbosity 100) (beep-bash-name "NOTES"))
    (beep-play-string
"c2q ebes ce cs fe c bb1 c2q ges ce cs abe g eb
c g c3 c2s bb1e bb1s ge d2 cq")))



(defun beep-demo-updown ()
  (interactive)
  (let ((beep-verbosity 100) (beep-bash-name "Up Down"))
    (beep-play-string
     
     "co3v3 d e f g a b co4 z co4 bo3 a g f e d c

## Note that co3e==co3v2==c3e. (e for eigth)
## co3v3 just sets the current octave: 3, and the current duration: e
## for eigth, which is same as v3.
## The 3 in c3e was optional, since the default octave was already 3.
## Further notes are played with the same octave and duration as the
## current one.  That is, until we reach the next end of the
## spectrum.  There we increase the octave by 1, to make it 4.  Next,
## we pause for a moment via z.  Next, we have to come back. So, we
##stick with c, but next, we have to tell it to lower the octave. So, we
##stick a 3 with b, to make it b3 or bo3. We could have also done o3 b

")))


(defun beep-demo-updown2 ()
  (interactive)
  (let ((beep-verbosity 100) (beep-bash-name "Up Down 2"))
    (beep-play-string
"v3  c d e zv5
v3 d e f zv5
v3  v3 e f g zv5
v3  v3 f g a zv5
v3  v3 g a b zv5
v3  a b c4 zv3
v3 c4 b3 a zv5
v3  b a g zv5
v3  a g f zv5
v3  g f e  zv5
v3  f e d zv5
v3  e d c zv5
## Newlines are used just for coding clarity.
## Before At each triplet, we set the default speed to 3.
## Except at the end, where we pause for a smaller moment via v5.
## If we had used V5, we could have avoided most of the remaining v3s
## because V would have left the default alone.")))


(defun beep-demo-o_susanna ()
  (interactive)
  (let ((beep-verbosity 100))
    (beep-play-string

     ;; in this string, we start by increasing the at the beginning
     ;; (from its default of 3)
     ;; Of course, note that that happens only for the duration of the
     ;; song.
  "o4 cqe de eq g gqe ae gq e cqe de eq e d c dh cqe de eq g gqe ae gq e
cqe de eq e d d cw fh f aq ah aq g g e c dh cqe de eq g gqe ae gq e
cqe de eq e d d c")))




;;====================================================

(defcustom  beep-synth-buffer "*beep-synthesizer*" ""
  :group 'beep)


(defvar beep-mode-map-default
  '(keymap))





(define-key beep-mode-map-default
  (kbd "SPC") 'beep-self-insert-space)


(define-key beep-mode-map-default
  (kbd "C-c C-c") 'beep-play-buffer)

(define-key beep-mode-map-default
  (kbd "C-c C-x C-e") 'beep-play-region)



(defcustom beep-mode-map beep-mode-map-default
  "Change this to what yoyu like inn your .emacs."
  :group 'beep)

(defun beep-self-insert-space ()
  (interactive)
  (insert " ")
  (beep-synth-play-note-at-point))

(defun beep-synth-play-note-at-point ()
  (let* ((str (buffer-substring-no-properties (point-min) (point)))
         (commands
          (beep-freqduration->shell-commands
           (beep-lisp->freqduration
            (beep-notes->lisp str)))))
    (shell-command
     (first (last commands)))))



(defcustom beep-mode-string " BEEP" "" :group 'beep)

;;;###autoload
(easy-mmode-define-minor-mode
 beep-mode
 "The mode to inherit minibuffer keybindings"
 nil
 beep-mode-string
 ;; 3 means C-c
 ;; 16 means C-p
 'beep-mode-map)

(defcustom beep-synth-init-string "\nv3 o3 " "" :group 'beep)

;;;###autoload
(defun beep-synthesizer ()
  (interactive)
  (switch-to-buffer
   (get-buffer-create beep-synth-buffer))
  (text-mode)
  (goto-char (point-max))
  (insert beep-synth-init-string)
  (beep-mode 1))

;;;###autoload
(defalias 'beep-player 'beep-synthesizer)
(defalias 'beep-synth 'beep-synthesizer)

;; ====================================================

(defun beep-sh-help ()
  (require 'shs)
  (shsm
   "Syntax: 
beep.sh [:o <num>] [:v <num>] (:f file) || (:p <string>)>
Use :o to specify octave number, v for velocity (speed).
:f to play a file,
or :p to specify a string to play.  Do quote the string. 

Use :h for this help.  For some example usage from bash commandline,
see the source code of the file beep.sh


"))
  
(defun* beep-sh (&key (o 0) (v 0) help h f p)
  " Note that you don't need this.  beep.el can generate bash
  equivalents for you which you can directly use from bash in any
  case. 

But, if you still want to be able to call beep.el from bash, you can
use this function and the attached file beep.sh.

Useful for running beep.el from commandline or scripts. 
Install shs.el, shs-utils.el, beep.sh, and type something like:
beep.sh :p \"v3 o3 c d e f g a b c4\"


The keyword o INCREASES the overall octave by its argument. 
The keyword v INCREASES the overall speed by its argument. 
In other words, they both modify beep-.*-user variables. 

"
;; beep.sh :p \"v3 o3 c d e f g a b c4\"
  (require 'shs-utils)
  (let ((beep-tmp 0))
    (if (or h 
            help
            (or (and (null f) (null p)))
            (string-match "help" (format "%s%s%s%s" o v h help)))
        (beep-sh-help)
      ;; else
      (setq beep-param-speed-user 
            (shsu-arg-numerify v))
      (setq beep-param-octave-user 
            (shsu-arg-numerify o))
      (cond
       (f (beep-play-file f))
       (t (beep-play-string
           (format "%s" p)))))))


  


        



(provide 'beep)
(run-hooks 'beep-after-load-hook)



;;; beep.el ends here





reply via email to

[Prev in Thread] Current Thread [Next in Thread]