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

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

synth.el v. 0.2dev


From: D Goel
Subject: synth.el v. 0.2dev
Date: Sun, 31 Jul 2005 19:34:18 -0400
User-agent: Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux)

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

(The 2 bash scripts:

 * beep_to_speaker.sh
 * command-line invocation of synth.el

 ...  which I posted separately a few hours ago, are probably briefly
held up for moderator-review. If they don't show up by tomorrow, I
will add "add-on for synth.el" (which, it seems, makes them pass
automatic mod-check) to the subject and resend them. )





New Features since 0.1
======================
* A typo corrected everywhere in the M-x synth-choose-speaker section,
that makes the whole section finally make sense to the user.

New Features since 0.0
======================

* beep.el is now (more sanely) known as synth.el, following a name-change
  suggestion by RMS.


* The new letter r also stands for 'rest'.  This is an alternative to
  the z we already had in synth.el.  This is for ditty compatibility.

* RMS suggested it be made to work with pc speakers as well.  I
  could't find a synth-like program that plays to sound speakers (if
  you know one, please let me know), so I wrote my own
  beep_to_speaker.sh (posted separately).  Therefore:

* There are two new mechanisms to beep via soundcard instead of the pc
  speaker.  These are called "speaker continuous" and "speaker
  discontinuous".  In the synthesizer mode, you can see the currently
  active speaker mechanism in the modeline. 


* To choose a speaker mechanism, type M-x
  synth-options-choose-speaker.  As you (will) see, this needs
  querer.el installed, which I am posting here separately.  The
  alternative is to customise synth-fd->program-function by hand.

* When using soundcard, we can now allow for volume control as
  well. Volume is referred to as magnitude m in synth
  terminology. (Any volume directives are silently ignored when using
  the pc speaker) .  In your music sheets, use m<num> to increase or
  decrease the magnitude.  (we could not use v for volume, since v is
  taken for velocity).  As a user, you can control overall volume via
  M-x beep-options-magnitude-<increase/decrease>.

* For soundcard, we use the external script beep_to_soundcard.sh
  instead of synth.  That script is posted here separately.  To use that, you
  will need to apt-get install octave2.1 AND apt-get install
  octave-forge.  Drop that script somewhere in your bash path, like
  ~/bin/


* New demo for loudness variation: M-x synth-demo-loudness

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

INTRODUCTION:
============
synth.el is an interface between the music sheet spec and a
corresponding synth player.  It can help you synthesize music,
converting your keystrokes a,bq,c#, etc. to frequency+duration+volume
as you type them, and then playing them through your pc-speaker.  Or,
you can play music from such text-based music files.  Or just have fun
with the current buffer and see M-x synth-play-buffer what it sounds
like.  It can generate a bash script for the current music sheet, the
script uses synth 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 synth, and then type M-x synth-demo-<name>.

Next, fiddle with user options like M-x synth-options-octave-increase
or M-x synth-options-speed-decrease, M-x
synth-options-magnitude-increase, etc. and replay the demo.  As you can
see, synth.el also generates the equivalent bash scripts for your use
for things like crontab reminders.  Don't forget to reset options
using M-x synth-options-reset (this does not reset the speaker choice).


Next, type M-x synthesizer (or M-x synth-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 synth-eval-buffer, or C-c C-c.

Magnitude (volume) options take effect only when using sound speakers.

M-x synth-options-choose-speaker chooses speaker type, currently, we
offer pc-speaker (default) and 2 sound speaker choices.  


Type M-x synth-commentary for many more comments, syntax, etc.

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

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

;;; synth.el --- music sheet player, synthesizer, music via bash scripts
;; Time-stamp: <2005-07-31 19:30:21 deego>
;; Copyright (C) 2005 D. Goel
;; Emacs Lisp Archive entry
;; Filename: synth.el
;; Package: synth
;; Author: D. Goel <address@hidden>
;; Keywords: crontab, bash music, sheet music player, synthesizer
;; Version:  0.2dev
;; URL: http://gnufans.net/~deego/emacspub/lisp-mine/synth/
;; For latest version:

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


 
;; 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 synth-quick-start
  "Type M-x synth-introduction
"
)

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

;;; Introduction:
;; Stuff that gets posted to gnu.emacs.sources
;; as introduction
(defconst synth-introduction
  "synth.el is an interface between the music sheet spec and a
corresponding synth player.  It can help you synthesize music,
converting your keystrokes a,bq,c#, etc. to frequency+duration+volume
as you type them, and then playing them through your pc-speaker.  Or,
you can play music from such text-based music files.  Or just have fun
with the current buffer and see M-x synth-play-buffer what it sounds
like.  It can generate a bash script for the current music sheet, the
script uses synth 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 synth, and then type M-x synth-demo-<name>.

Next, fiddle with user options like M-x synth-options-octave-increase
or M-x synth-options-speed-decrease, M-x
synth-options-magnitude-increase, etc. and replay the demo.  As you can
see, synth.el also generates the equivalent bash scripts for your use
for things like crontab reminders.  Don't forget to reset options
using M-x synth-options-reset (this does not reset the speaker choice).


Next, type M-x synthesizer (or M-x synth-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 synth-eval-buffer, or C-c C-c.

Magnitude (volume) options take effect only when using sound speakers.

M-x synth-options-choose-speaker chooses speaker type, currently, we
offer pc-speaker (default) and 2 sound speaker choices.  


Type M-x synth-commentary for many more comments, syntax, etc.

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

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

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



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

* A synth 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.

* The letter r is same as letter z, and means rest.


* 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.
  (we could not use s since s stands for sixteenth, see below)

* 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).

* m=> magnitude, or volume.  m0 is the default. m1 is double of m0,
  and so on. 

* 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
synth.el, type M-x synth-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  \"junk\" in an instruction is simply ignored.  We simply
  play the part of an instruction that we can parse.  But you should
  never rely on that as a feature.  **We will feel free to assign
  meaning to other letters, etc. in the future.**

* 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-ike 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.


 M<num>: refers to magnitude, or the volume.  0 is the default. M1 is
         double of M0, M-1 is half of M0, and so on.

 m, which we already introduced, is just like M, except the default
    volume also gets set.

 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).


* synth'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. synth.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 synth
  installed, apt-get install synth.



* 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
  synth-fd->program-my, and setq synth-fd->program-function
  to synth-fd->program-my.


* By default, we use the pc's built-in speaker rather than the sound
  cards, so that synth 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 synth.sh, whose interface and options are
  very similar to that of ditty.  synth.sh options: :f for filename
  synth.sh options: :t for speed or duration.  Each note is played for
  that long.


* Lisp: M-x synth-play-string is the wrapper which calls the various
  processors on a string.
 
  Processing: Each synth instruction is converted to a synth 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 synth lisp form
  (shell-command \"beep -f ...\").


 

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

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


* It is most instructive to see how the source code of M-x
  synth-demo-updown works.  The source code is explained there.  See
  also synth-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, magnitude etc.


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

"
)

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

;;; History:

;;; Bugs:

;;; New features:
(defconst synth-new-features
  "


New Features since 0.1
======================
* A typo corrected everywhere in the M-x synth-choose-speaker section,
that makes the whole section finally make sense to the user.

New Features since 0.0
======================

* beep.el is now (more sanely) known as synth.el, following a name-change
  suggestion by RMS.


* The new letter r also stands for 'rest'.  This is an alternative to
  the z we already had in synth.el.  This is for ditty compatibility.

* RMS suggested it be made to work with pc speakers as well.  I
  could't find a synth-like program that plays to sound speakers (if
  you know one, please let me know), so I wrote my own
  beep_to_speaker.sh (posted separately).  Therefore:

* There are two new mechanisms to beep via soundcard instead of the pc
  speaker.  These are called \"speaker continuous\" and \"speaker
  discontinuous\".  In the synthesizer mode, you can see the currently
  active speaker mechanism in the modeline. 


* To choose a speaker mechanism, type M-x
  synth-options-choose-speaker.  As you (will) see, this needs
  querer.el installed, which I am posting here separately.  The
  alternative is to customise synth-fd->program-function by hand.

* When using soundcard, we can now allow for volume control as
  well. Volume is referred to as magnitude m in synth
  terminology. (Any volume directives are silently ignored when using
  the pc speaker) .  In your music sheets, use m<num> to increase or
  decrease the magnitude.  (we could not use v for volume, since v is
  taken for velocity).  As a user, you can control overall volume via
  M-x beep-options-magnitude-<increase/decrease>.

* For soundcard, we use the external script beep_to_soundcard.sh
  instead of synth.  That script is posted here separately.  To use that, you
  will need to apt-get install octave2.1 AND apt-get install
  octave-forge.  Drop that script somewhere in your bash path, like
  ~/bin/


* New demo for loudness variation: M-x synth-demo-loudness
"


""
)

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



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

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

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

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

;;; Code:

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

(defcustom synth-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 'synth)
(defcustom synth-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 'synth)
(defcustom synth-y-or-n-p-function 'synth-y-or-n-p
  "Function to use for interactivity-dependent  `y-or-n-p'.
Format same as that of `synth-y-or-n-p'."
  :type 'function
  :group 'synth)
(defcustom synth-n-or-y-p-function 'synth-n-or-y-p
  "Function to use for interactivity-dependent `n-or-y-p'.
Format same as that of `synth-n-or-y-p'."
  :type 'function
  :group 'synth)
(defun synth-message (points &rest args)
  "Signal message, depending on POINTS and `synth-verbosity'.
ARGS are passed to `message'."
  (unless (minusp (+ points synth-verbosity))
    (apply #'message args)))
(defun synth-y-or-n-p (add prompt)
  "Query or assume t, based on `synth-interactivity'.
ADD is added to `synth-interactivity' to decide whether
to query using PROMPT, or just return t."
  (if (minusp (+ add synth-interactivity))
        t
      (funcall 'y-or-n-p prompt)))
(defun synth-n-or-y-p (add prompt)
  "Query or assume t, based on `synth-interactivity'.
ADD is added to `synth-interactivity' to decide whether
to query using PROMPT, or just return t."
  (if (minusp (+ add synth-interactivity))
        nil
      (funcall 'y-or-n-p prompt)))

;;; Real Code:


(defun synth-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 synth-log-current-string nil)
(defvar synth-log-current-stringlist  nil)


(defun synth-check-maybe ()
  (when (> synth-verbosity 30)
    
    (let ((synthp (> (length (shell-command-to-string "which beep")) 3)))
      (unless synthp
        (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)))))



(defcustom synth-string->program-function 
  'synth-string->program-function-default
  "" :group 'synth)




(defun synth-play-string (str)
  (interactive "s")
  ;;(synth-check-maybe)
  (synth-log-clear)

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


(defcustom synth-fd->program-function-choices
  '(synth-fd->program-beep-pcspeaker 
    synth-fd->program-beep-soundcard-continuous
    synth-fd->program-beep-soundcard-discontinuous

)


  ""
  :group 'synth)



(defcustom synth-fd->program-function 
  'synth-fd->program-beep-pcspeaker
  "Frob this variable for your custom synth solutions.  You might want
to use M-x `synth-choose-speaker' to set this variable for the current
session.  See the code of `synth-fd->program-default' for an
example.  That code is just a wrapper for the 4 processing stages.  In
your custom solution, you can replace any of those stages.  If you
come up with a nice alternative to anything, do contribute it to
synth.el "
  :group 'synth
  ;; why doesn't this work as I would expect?
  ;;:options  synth-fd->program-function-choices
  ;;:type '(choice (const abc bcd))

)




(defun synth-string->program-function-default (str)
  (funcall synth-fd->program-function 
           (synth-lisp->freqduration
            (synth-notes->lisp str))))



(defun synth-fd->program-beep (fd)
  (synth-shell-commands->program
   (synth-freqduration->shell-commands
    fd)))

(defalias 'synth-fd->program-beep-pcspeaker
  'synth-fd->program-beep)


(defun synth-fd->program-beep-soundcard-discontinuous (fd)
  (let ((synth-command-format synth-command-format-synth-soundcard))
    (synth-fd->program-beep fd)))


(defun synth-fd->program-beep-soundcard-continuous (fd)
  (synth-shell-commands->program
   (synth-freqduration->shell-commands-synth-soundcard-continuous
    fd)))



(defun synth-choose-speaker ()
  (interactive)
  (let (
        ;;(choices synth-fd->program-function-choices)
        (qp (ignore-errors (require 'querer))))
    (unless qp
      (error 
       "This function needs querer.el installed. 
Alternatively, you can use defcustom on
`synth-sring-to-program-function'.
The final alternative is to manually setq it in .emacs"))
    
    (let* (
           (querer-bindings-extra nil)
           (qresult
            (querer-auto-eval
             (mapcar 
              (lambda (a)
                `(quote ,a))
              synth-fd->program-function-choices)
             'auto 
             "Type C-g to abort"
             (mapcar 'synth-get-nick-speaker-type 
                     synth-fd->program-function-choices))))
      (if (functionp qresult)
          (setq synth-fd->program-function qresult)
        (error 
         "You did not select a speaker type.  I will leave italone."))
      (message "Synth main program set to %s" 
               synth-fd->program-function))))











       

      



(defalias 'synth-fd->program-default
  'synth-fd->program-beep)



(defcustom synth-command-format
  "beep -f %s -l %s" 
"Leave this variable alone unless you know what you are doing. "
:group 'synth
)


(defcustom synth-command-format-synth-soundcard
  "beep_to_speaker.sh %s %s %s"
  "Leave this variable alone unless you know what you are doing. "
:group 'synth
)



(defun synth-freqduration->shell-commands (fdm)
  "Negative freq, means no-op."
  (let ((shells
         (mapcar
          (lambda (arg)
            (let ((f (car arg))
                  (d (second arg))
                  (m (third 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 synth-command-format 
                        f d m))))
          fdm)))
    (synth-log-as-bash-script
     ;;(mapconcat 'identity shells "\n")
     (mapconcat
      'identity
      (mapcar* (lambda (a b) (concat a " # " b))
               shells synth-log-current-stringlist)
      "\n"))
    shells))


;;;###autoload
(defun synth-flatten-tree (tree)
  "Copied by D. Goel from erbutils.el, by the same author"
  (cond
   ((null tree) nil)
   ((listp tree) (apply 'append
      (mapcar 'synth-flatten-tree tree)))
   (t (list tree))))


(defun synth-freqduration->shell-commands-synth-soundcard-continuous (fdms)
  "Negative freq, means no-op."
  (let* ((fdms2 (copy-tree fdms))
         fdmthis
         (fdmsflat nil)
         shellcmd
         )
    (while fdms2
      (setq fdmthis (pop fdms2))
      (while (not (= 3 (length fdmthis)))
        (setq fdmthis (append fdmthis (list -1))))
      (setq fdmsflat (append fdmsflat fdmthis)))
    (setq shellcmd 
          (concat 
           "beep_to_speaker.sh "
           (mapconcat 
            #'(lambda (n)
                (format "%s" n))
            fdmsflat " ")))
    (synth-log-as-bash-script
     ;;(mapconcat 'identity shells "\n")
     (concat shellcmd "\n"))
    (list shellcmd)))





  



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


(defun synth-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 synth-log (arg &optional suppressp)
  (save-excursion
    (set-buffer (get-buffer-create "*synth-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 synth-log-clear ()
  (save-excursion
    (set-buffer (get-buffer-create "*synth-log*"))
    (delete-region (point-min) (point-max))))


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

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






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







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

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

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

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



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

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

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


(defconst synth-volume 400.00
  "change magnitude instead, no need to change
 this var. ")




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

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


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


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

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

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



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

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






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

(defun synth-options-magnitude-increase (&optional num)
  "Increase volume"
  (interactive "p")
  (setq synth-param-magnitude-user
        (+ num synth-param-magnitude-user))
  (message "User Magnitude set to %s" synth-param-magnitude-song))



(defun synth-options-magnitude-decrease (&optional num)

  (interactive "p")
  (synth-options-magnitude-increase (- num)))

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


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

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



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

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


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

(defun synth-options-octave-increase (num)
  (interactive "p")
  (setq synth-param-octave-user
        (+ num synth-param-octave-user)))




(defalias 'synth-options-tempo-increase 'synth-options-octave-increase)

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


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

(defun synth-options-reset ()
  "This does not reset the speaker choice."
  (interactive)
  (setq synth-param-magnitude-user 0)
  (setq synth-param-octave-user 0)
  (setq synth-param-speed-user 0))






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

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



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

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



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

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





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

(defun synth-note-to-lisp (note)
  (assert (stringp note))
  ;; convert any but the first e's into v3's.
  (while
      (string-match "^.*[a-gA-GrzRZ].*\\(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 (synth-note-to-lisp-break-note note))
       (subnoteslisp1
        (apply
         'append
         (mapcar
          (if sharpp 'synth-subnote-to-lisp-sharped 'synth-subnote-to-lisp )
          subnotes))))
    (synth-lisp-note-cleanup subnoteslisp1)))

(defun synth-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 synth-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 synth-subnote-to-lisp-sharpp nil)
(defun synth-subnote-to-lisp-sharped (subnote)
  (let ((synth-subnote-to-lisp-sharpp t))
    (synth-subnote-to-lisp subnote)))

(defun synth-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 ?r ?R ?z ?Z))
        (append (list
                 (list
                  'synth-command-letter
                  (intern
                   (string
                    (if synth-subnote-to-lisp-sharpp
                        (upcase bc1)
                      bc1)))))
                ;; The rest of the string may be a number.. in which
                ;; case it should be parsed.
                (synth-subnote-to-lisp bcreststr)))
       ((member bc1 '(?m))
        (list (list 'synth-command-m restnum)))
       ((member bc1 '(?M))
        (list (list 'synth-command-M restnum)))
       ((member bc1 '(?o))
        (list (list 'synth-command-o restnum)))
       ((member bc1 '(?O))
        (list (list 'synth-command-O restnum)))
       ((member bc1 '(?v))
        (list (list 'synth-command-v restnum)))
       ((member bc1 '(?V))
        (list (list 'synth-command-V restnum)))
       ;; add sharps to the command.. even though sharpening should have already
       ;; been taken care of..
       ((member bc1 '(?#))
        (append (list (list 'synth-command-sharp))
                (synth-subnote-to-lisp bcreststr)))


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

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

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

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


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

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


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


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

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



(defun synth-song-defaults ()
  (setq synth-param-magnitude synth-param-magnitude-song)
  (setq synth-param-speed synth-param-speed-song)
  (setq synth-param-octave synth-param-octave-default))

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

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

(defun synth-lisp->freqduration-once (note)
  (if note
      (let ((synth-working-freq 1)

            (synth-working-octave synth-param-octave)
            (synth-working-magnitude synth-param-magnitude)
            (synth-working-speed synth-param-speed)

            (synth-working-found-letter-p nil)
            (synth-working-duration synth-duration)

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

          (setq synth-working-speed
                (synth-working-speed-from-speed-list
                 synth-working-speed-working))
          (when synth-working-speed-save-p
            (setq synth-param-speed synth-working-speed)))
     
        (if synth-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,..
             (* synth-working-freq
                (expt 2.0 (+ synth-working-octave
                             synth-param-octave-user
                             -3)))
             (/ (float synth-working-duration)
                (expt 2.0
                      (+ synth-working-speed
                         synth-param-speed-user)))

             (* (float synth-volume)
                (expt 2.0
                      (+ synth-working-magnitude
                         synth-param-magnitude-user)))




             )
          
          (list -1 0 0)))
    (list -1 0 0)))


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



(defun synth-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. "
  (synth-log2
   (/ 1.0
      (apply '+
             (mapcar  (lambda (arg)
                        (expt 2.0 (- arg)))
                      tlist)))))

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

(defun synth-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
    (synth-command-letter
     (setq synth-working-found-letter-p t)
     (setq synth-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!!

             (r 0.01); pause
             (R 0.01); pause
             (z 0.01); pause
             (Z 0.01); pause

             ;;C         523.2
             (otherwise 1))))

    (synth-command-M
     (setq synth-working-magnitude arg))


    (synth-command-m
     (setq synth-working-magnitude arg)
     (setq synth-param-magnitude arg))


    (synth-command-O
     (setq synth-working-octave arg))

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

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

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


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


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


    
                                      


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

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

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

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

         

;;;###autoload
(defun synth-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 synth-demo-<name> instead."))

(defun synth-demo-furelise ()
  (interactive)
  (let ((synth-verbosity 100)
        (synth-bash-name "Fur Elise"))
    (synth-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 synth-demo-notes ()
  (interactive)
  (let ((synth-verbosity 100) (synth-bash-name "NOTES"))
    (synth-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 synth-demo-updown ()
  (interactive)
  (let ((synth-verbosity 100) (synth-bash-name "Up Down"))
    (synth-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 synth-demo-updown2 ()
  (interactive)
  (let ((synth-verbosity 100) (synth-bash-name "Up Down 2"))
    (synth-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 synth-demo-loudness ()
  (interactive)
  (let ((synth-verbosity 100) (synth-bash-name "Up Down")
        ;; this is normaally done by M-x synth-choose-speaker
        (synth-fd->program-function 
         'synth-fd->program-beep-soundcard-continuous
         ))
    (synth-play-string

     "v3 o3 am-4 am-3 am-2 am-2.5 am-1 am-1.5 am-.5 am0 am.5 am1 am1.5
am2 am2.5 am3 am4 am5 am5 am4 am3 am2.5 am2 am1.5 am1 am.5 am0
am-.5 am-1 am-1.5 am-2 am-2.5 am-3 am-4 
## play the same note but with different volumes
")))





(defun synth-demo-o_susanna ()
  (interactive)
  (let ((synth-verbosity 100))
    (synth-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  synth-synth-buffer "*synth-synthesizer*" ""
  :group 'synth)


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





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


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

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



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

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

(defun synth-synth-play-note-at-point ()
  (let* ((str (buffer-substring-no-properties (point-min) (point)))
         (fd
          (synth-lisp->freqduration
           (synth-notes->lisp str))))
    (eval 
     (funcall synth-fd->program-function 
              (last fd)))))











(defun synth-get-nick-speaker-type (fn)
  (let ((fstr (format "%s" fn)))
    (if 
        (string-match
         "^synth-fd->program-\\(.*\\)$"
         fstr)
        (replace-regexp-in-string "-" " "
                                  (match-string 1 fstr))
      fstr)))


(defcustom synth-mode-string  
  "SYNTH"
  "" :group 'synth)
(defcustom synth-mode-string-format  " %s (%s)" "" :group 'synth)

;;;###autoload
(easy-mmode-define-minor-mode
 synth-mode
 "The mode to inherit minibuffer keybindings"
 nil
 (:eval (format synth-mode-string-format synth-mode-string 
                (synth-get-nick-speaker-type 
                 synth-fd->program-function)))
 ;; 3 means C-c
 ;; 16 means C-p
 'synth-mode-map)

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

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


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


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

(defun synth-sh-help ()
  (require 'shs)
  (shsm
   "Syntax: 
synth.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 synth.sh


"))
  
(defun* synth-sh (&key (o 0) (v 0) help h f p)
  " Note that you don't need this.  synth.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 synth.el from bash, you can
use this function and the attached file synth.sh.

Useful for running synth.el from commandline or scripts. 
Install shs.el, shs-utils.el, synth.sh, and type something like:
synth.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 synth-.*-user variables. 

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


(defalias 'synth-options-choose-speaker 'synth-choose-speaker)


  


        



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



;;; synth.el ends here





reply via email to

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