emacs-diffs
[Top][All Lists]
Advanced

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

[Emacs-diffs] /srv/bzr/emacs/trunk r103555: * calc/calc-units.el (math-m


From: Jay Belanger
Subject: [Emacs-diffs] /srv/bzr/emacs/trunk r103555: * calc/calc-units.el (math-midi-round, math-freqp, math-midip)
Date: Sat, 05 Mar 2011 22:28:39 -0600
User-agent: Bazaar (2.0.3)

------------------------------------------------------------
revno: 103555
committer: Jay Belanger <address@hidden>
branch nick: trunk
timestamp: Sat 2011-03-05 22:28:39 -0600
message:
  
  * calc/calc-units.el (math-midi-round, math-freqp, math-midip)
    (math-spnp, math-spn-to-midi, math-midi-to-spn, math-freq-to-spn)
    (math-midi-to-freq, math-spn-to-freq, calcFunc-spn, calcFunc-midi)
    (calcFunc-freq, calc-freq, calc-midi, calc-spn): New functions.
    (math-notes): New variable.
  
  * calc/calc.el (calc-note-threshold): New variable.
  
  * calc/calc-ext.el (calc-init-extensions): Add keybindings for
    calc-spn, calc-midi, calc-freq.  Add autoloads for calcFunc-spn,
    calcFunc-midi, calcFunc-freq, calc-spn, calc-midi and calc-freq.
  
  * doc/misc/calc.tex (Musical Notes): New section.
    (Customizing Calc): Mention calc-note-threshold.
modified:
  doc/misc/ChangeLog
  doc/misc/calc.texi
  lisp/ChangeLog
  lisp/calc/calc-ext.el
  lisp/calc/calc-units.el
  lisp/calc/calc.el
=== modified file 'doc/misc/ChangeLog'
--- a/doc/misc/ChangeLog        2011-03-06 03:51:28 +0000
+++ b/doc/misc/ChangeLog        2011-03-06 04:28:39 +0000
@@ -3,6 +3,9 @@
        * calc.texi (Logarithmic Units): Rename calc-logunits-dblevel
        and calc-logunits-nplevel to calc-dblevel and calc-nplevel,
        respectively.
+       (Musical Notes): New section.
+       (Customizing Calc): Mention the customizable variable
+       calc-note-threshold.
 
 2011-03-03  Glenn Morris  <address@hidden>
 

=== modified file 'doc/misc/calc.texi'
--- a/doc/misc/calc.texi        2011-03-06 03:51:28 +0000
+++ b/doc/misc/calc.texi        2011-03-06 04:28:39 +0000
@@ -27676,6 +27676,7 @@
 * Predefined Units::
 * User-Defined Units::
 * Logarithmic Units::
+* Musical Notes::
 @end menu
 
 @node Basic Operations on Units, The Units Table, Units, Units
@@ -28121,7 +28122,7 @@
 is replaced by the new set.  (@xref{General Mode Commands}, for a way to
 tell Calc to use a different file for the Calc init file.)
 
address@hidden Logarithmic Units,  , User-Defined Units, Units
address@hidden Logarithmic Units, Musical Notes, User-Defined Units, Units
 @section Logarithmic Units
 
 The units @code{dB} (decibels) and @code{Np} (nepers) are logarithmic
@@ -28363,6 +28364,76 @@
 logarithmic unit by a number. Note that the reference quantities don't
 play a role in this arithmetic. 
 
address@hidden Musical Notes, , Logarithmic Units, Units
address@hidden Musical Notes
+
+Calc can convert between musical notes and their associated
+frequencies.  Notes can be given using either scientific pitch
+notation or midi numbers.  Since these note systems are basically
+logarithmic scales, Calc uses the @kbd{l} prefix for functions
+operating on notes.
+
+Scientific pitch notation refers to a note by giving a letter
+A through G, possibly followed by a flat or sharp) with a subscript
+indicating an octave number.  Each octave starts with C and ends with
+B and 
address@hidden increasing each note by a semitone will result
address@hidden in the sequence @expr{C}, @expr{C} sharp, @expr{D}, @expr{E} 
flat, @expr{E},
address@hidden @expr{F}, @expr{F} sharp, @expr{G}, @expr{A} flat, @expr{A}, 
@expr{B}
address@hidden flat and @expr{B}.  
+the octave numbered 0 was chosen to correspond to the lowest
+audible frequency.  Using this system, middle C (about 261.625 Hz)
+corresponds to the note @expr{C} in octave 4 and is denoted
address@hidden  Any frequency can be described by giving a note plus an
+offset in cents (where a cent is a ratio of frequencies so that a
+semitone consists of 100 cents). 
+
+The midi note number system assigns numbers to notes so that
address@hidden(-1)} corresponds to the midi note number 0 and @expr{G_9}
+corresponds to the midi note number 127.   A midi controller can have
+up to 128 keys and each midi note number from  0 to 127 corresponds to
+a possible key. 
+
address@hidden l s
address@hidden calc-spn
address@hidden spn
+The @kbd{l s} (@code{calc-spn}) address@hidden command converts either
+a frequency or a midi number to scientific pitch notation.  For
+example, @code{500 Hz} gets converted to 
address@hidden + 21.3094853649 cents} and @code{84} to @code{C_6}. 
+
+
address@hidden l m
address@hidden calc-midi
address@hidden midi
+The @kbd{l m} (@code{calc-midi}) address@hidden command converts either
+a frequency or a note given in scientific pitch notation to the
+corresponding midi number. For example, @code{C_6} gets converted to 84
+and @code{440 Hz} to 69.
+
address@hidden l f
address@hidden calc-freq
address@hidden freq
+The @kbd{l f} (@code{calc-freq}) address@hidden command converts either
+either a midi number or a note given in scientific pitch notation to
+the corresponding frequency. For example, @code{Asharp_2 + 30 cents}
+gets converted to @code{118.578040134 Hz} and @code{55} to
address@hidden Hz}.
+
+Since the frequencies of notes are not usually given exactly (and are
+typically irrational), the customizable variable
address@hidden determines how close (in cents) a frequency
+needs to be to a note to be recognized as that note
+(@pxref{Customizing Calc}).  This variable has a default value of
address@hidden  For example, middle @var{C} is approximately
address@hidden Hz}; this frequency is often shortened to
address@hidden Hz}.  Without @code{calc-note-threshold} (or a value of
address@hidden), Calc would convert @code{261.625 Hz} to scientific pitch
+notation @code{B_3 + 99.9962592773 cents}; with the default value of
address@hidden, Calc converts @code{261.625 Hz} to @code{C_4}.
+
+
+
 @node Store and Recall, Graphics, Units, Top
 @chapter Storing and Recalling
 
@@ -35481,6 +35552,15 @@
 @code{"20 uPa"}.  
 @end defvar
 
address@hidden calc-note-threshold
+See @ref{Musical address@hidden
+The variable @code{calc-note-threshold} is a number (written as a
+string) which determines how close (in cents) a frequency needs to be
+to a note to be recognized as that note.
+
+The default value of @code{calc-note-threshold} is 1.
address@hidden defvar
+
 @defvar calc-highlight-selections-with-faces
 @defvarx calc-selected-face
 @defvarx calc-nonselected-face
@@ -36129,26 +36209,29 @@
 @r{    v x@:    I k T   @:             @:        @:ltpt@:(x,v)}
 
 @c
address@hidden    a b@:      l +   @:             @:     2  @:lupoweradd@:(a,b)}
address@hidden    a b@:    H l +   @:             @:     2  @:lufieldadd@:(a,b)}
address@hidden    a b@:      l -   @:             @:     2  @:lupowersub@:(a,b)}
address@hidden    a b@:    H l -   @:             @:     2  @:lufieldsub@:(a,b)}
address@hidden    a b@:      l *   @:             @:     2  @:lupowermul@:(a,b)}
address@hidden    a b@:    H l *   @:             @:     2  @:lufieldmul@:(a,b)}
address@hidden    a b@:      l /   @:             @:     2  @:lupowerdiv@:(a,b)}
address@hidden    a b@:    H l /   @:             @:     2  @:lufielddiv@:(a,b)}
address@hidden      a@:      l d   @:             @:     1  @:dbpowerlevel@:(a)}
address@hidden    a b@:    O l d   @:             @:     2  
@:dbpowerlevel@:(a,b)}
address@hidden      a@:    H l d   @:             @:     1  @:dbfieldlevel@:(a)}
address@hidden    a b@:  O H l d   @:             @:     2  
@:dbfieldlevel@:(a,b)}
address@hidden      a@:      l n   @:             @:     1  @:nppowerlevel@:(a)}
address@hidden    a b@:    O l n   @:             @:     2  
@:nppowerlevel@:(a,b)}
address@hidden      a@:    H l n   @:             @:     1  @:npfieldlevel@:(a)}
address@hidden    a b@:  O H l n   @:             @:     2  
@:npfieldlevel@:(a,b)}
address@hidden      a@:      l q   @:             @:     1  @:powerquant@:(a)}
address@hidden    a b@:    O l q   @:             @:     2  @:powerquant@:(a,b)}
address@hidden      a@:    H l q   @:             @:     1  @:fieldquant@:(a)}
address@hidden    a b@:  O H l q   @:             @:     2  @:fieldquant@:(a,b)}
address@hidden    a b@:      l +   @:             @:        @:lupoweradd@:(a,b)}
address@hidden    a b@:    H l +   @:             @:        @:lufieldadd@:(a,b)}
address@hidden    a b@:      l -   @:             @:        @:lupowersub@:(a,b)}
address@hidden    a b@:    H l -   @:             @:        @:lufieldsub@:(a,b)}
address@hidden    a b@:      l *   @:             @:        @:lupowermul@:(a,b)}
address@hidden    a b@:    H l *   @:             @:        @:lufieldmul@:(a,b)}
address@hidden    a b@:      l /   @:             @:        @:lupowerdiv@:(a,b)}
address@hidden    a b@:    H l /   @:             @:        @:lufielddiv@:(a,b)}
address@hidden      a@:      l d   @:             @:        @:dbpowerlevel@:(a)}
address@hidden    a b@:    O l d   @:             @:        
@:dbpowerlevel@:(a,b)}
address@hidden      a@:    H l d   @:             @:        @:dbfieldlevel@:(a)}
address@hidden    a b@:  O H l d   @:             @:        
@:dbfieldlevel@:(a,b)}
address@hidden      a@:      l n   @:             @:        @:nppowerlevel@:(a)}
address@hidden    a b@:    O l n   @:             @:        
@:nppowerlevel@:(a,b)}
address@hidden      a@:    H l n   @:             @:        @:npfieldlevel@:(a)}
address@hidden    a b@:  O H l n   @:             @:        
@:npfieldlevel@:(a,b)}
address@hidden      a@:      l q   @:             @:        @:powerquant@:(a)}
address@hidden    a b@:    O l q   @:             @:        @:powerquant@:(a,b)}
address@hidden      a@:    H l q   @:             @:        @:fieldquant@:(a)}
address@hidden    a b@:  O H l q   @:             @:        @:fieldquant@:(a,b)}
address@hidden      a@:      l s   @:             @:        @:spn@:(a)}
address@hidden      a@:      l m   @:             @:        @:midi@:(a)}
address@hidden      a@:      l f   @:             @:        @:freq@:(a)}
 
 @c
 @r{       @:      m a   @:             @: 12,13  @:calc-algebraic-mode@:}

=== modified file 'lisp/ChangeLog'
--- a/lisp/ChangeLog    2011-03-06 03:51:28 +0000
+++ b/lisp/ChangeLog    2011-03-06 04:28:39 +0000
@@ -3,10 +3,20 @@
        * calc/calc-ext.el (calc-init-extensions):
        Rename calc-logunits-dblevel and calc-logunits-nplevel to
        calc-dblevel and calc-nplevel, respectively.
+       Add keybindings for calc-spn, calc-midi and calc-freq.  Add
+       autoloads for calcFunc-spn, calcFunc-midi, calcFunc-freq,
+       calc-spn, calc-midi and calc-freq.
 
        * calc/calc-units.el (calc-dblevel): Rename from
        calc-logunits-dblevel.
        (calc-nplevel): Rename from calc-logunits-nplevel.
+       (math-midi-round, math-freqp, math-midip, math-spnp)
+       (math-spn-to-midi, math-midi-to-spn, math-freq-to-spn)
+       (math-midi-to-freq, math-spn-to-freq, calcFunc-spn, calcFunc-midi)
+       (calcFunc-freq, calc-freq, calc-midi, calc-spn): New functions.
+       (math-notes): New variable.
+
+       * calc/calc.el (calc-note-threshold): New variable.
 
 2011-03-06  Chong Yidong  <address@hidden>
 

=== modified file 'lisp/calc/calc-ext.el'
--- a/lisp/calc/calc-ext.el     2011-03-06 03:51:28 +0000
+++ b/lisp/calc/calc-ext.el     2011-03-06 04:28:39 +0000
@@ -429,6 +429,10 @@
   (define-key calc-mode-map "l-" 'calc-logunits-sub)
   (define-key calc-mode-map "l*" 'calc-logunits-mul)
   (define-key calc-mode-map "l/" 'calc-logunits-divide)
+  (define-key calc-mode-map "ls" 'calc-spn)
+  (define-key calc-mode-map "lm" 'calc-midi)
+  (define-key calc-mode-map "lf" 'calc-freq)
+  
   (define-key calc-mode-map "l?" 'calc-l-prefix-help)
 
   (define-key calc-mode-map "m" nil)
@@ -944,7 +948,7 @@
 calcFunc-lufieldmul calcFunc-lupowermul calcFunc-lufielddiv
 calcFunc-lupowerdiv calcFunc-fieldquant calcFunc-powerquant
 calcFunc-dbfieldlevel calcFunc-dbpowerlevel calcFunc-npfieldlevel
-calcFunc-nppowerlevel
+calcFunc-nppowerlevel calcFunc-spn calcFunc-midi calcFunc-freq
 math-build-units-table math-build-units-table-buffer
 math-check-unit-name math-convert-temperature math-convert-units
 math-extract-units math-remove-units math-simplify-units
@@ -1178,7 +1182,8 @@
 calc-remove-units calc-simplify-units calc-undefine-unit
 calc-view-units-table calc-logunits-quantity calc-dblevel
 calc-nplevel calc-logunits-add calc-logunits-sub
-calc-logunits-mul calc-logunits-divide)
+calc-logunits-mul calc-logunits-divide calc-spn calc-midi
+calc-freq)
 
  ("calc-vec" calc-arrange-vector calc-build-vector calc-cnorm
 calc-conj-transpose calc-cons calc-cross calc-kron calc-diag

=== modified file 'lisp/calc/calc-units.el'
--- a/lisp/calc/calc-units.el   2011-03-06 03:51:28 +0000
+++ b/lisp/calc/calc-units.el   2011-03-06 04:28:39 +0000
@@ -1859,6 +1859,221 @@
          (calc-binary-op "lunp" 'calcFunc-nppowerlevel arg)
        (calc-unary-op "lunp" 'calcFunc-nppowerlevel arg)))))
 
+;;; Musical notes
+
+
+(defvar calc-note-threshold)
+
+(defun math-midi-round (num)
+  "Round NUM to an integer N if NUM is within calc-note-threshold cents of N."
+  (let* ((n (math-round num))
+         (diff (math-abs
+                (math-sub num n))))
+    (if (< (math-compare diff (math-read-expr calc-note-threshold)) 0)
+        n
+      num)))
+
+(defconst math-notes
+  '(((var C var-C) . 0)
+    ((var Csharp var-Csharp) . 1)
+;    ((var C♯ var-C♯) . 1)
+    ((var Dflat var-Dflat) . 1)
+;    ((var D♭ var-D♭) . 1)
+    ((var D var-D) . 2)
+    ((var Dsharp var-Dsharp) . 3)
+;    ((var D♯ var-D♯) . 3)
+    ((var E var-E) . 4)
+    ((var F var-F) . 5)
+    ((var Fsharp var-Fsharp) . 6)
+;    ((var F♯ var-F♯) . 6)
+    ((var Gflat var-Gflat) . 6)
+;    ((var G♭ var-G♭) . 6)
+    ((var G var-G) . 7)
+    ((var Gsharp var-Gsharp) . 8)
+;    ((var G♯ var-G♯) . 8)
+    ((var A var-A) . 9)
+    ((var Asharp var-Asharp) . 10)
+;    ((var A♯ var-A♯) . 10)
+    ((var Bflat var-Bflat) . 10)
+;    ((var B♭ var-B♭) . 10)
+    ((var B var-B) . 11))
+  "An alist of notes with their number of semitones above C.")
+
+(defun math-freqp (freq)
+  "Non-nil if FREQ is a positive number times the unit Hz.
+If non-nil, return the coefficient of Hz."
+  (let ((freqcoef (math-simplify-units
+                   (math-div freq '(var Hz var-Hz)))))
+    (if (Math-posp freqcoef) freqcoef)))
+
+(defun math-midip (num)
+  "Non-nil if NUM is a possible MIDI note number.
+If non-nil, return NUM."
+  (if (Math-numberp num) num))
+
+(defun math-spnp (spn)
+  "Non-nil if NUM is a scientific pitch note (note + cents).
+If non-nil, return a list consisting of the note and the cents coefficient."
+  (let (note cents rnote rcents)
+    (if (eq (car-safe spn) '+)
+        (setq note (nth 1 spn)
+              cents (nth 2 spn))
+      (setq note spn
+            cents nil))
+    (cond
+     ((and  ;; NOTE is a note, CENTS is nil or cents.
+       (eq (car-safe note) 'calcFunc-subscr)
+       (assoc (nth 1 note) math-notes)
+       (integerp (nth 2 note))
+       (setq rnote note)
+       (or 
+        (not cents)
+        (Math-numberp (setq rcents
+                            (math-simplify 
+                             (math-div cents '(var cents var-cents)))))))
+      (list rnote rcents))
+     ((and  ;; CENTS is a note, NOTE is cents.
+       (eq (car-safe cents) 'calcFunc-subscr)
+       (assoc (nth 1 cents) math-notes)
+       (integerp (nth 2 cents))
+       (setq rnote cents)
+       (or 
+        (not note)
+        (Math-numberp (setq rcents
+                            (math-simplify 
+                             (math-div note '(var cents var-cents)))))))
+      (list rnote rcents)))))
+
+(defun math-freq-to-midi (freq)
+  "Return the midi note number corresponding to FREQ Hz."
+  (let ((midi (math-add
+               69
+               (math-mul
+                12
+                (calcFunc-log
+                 (math-div freq 440)
+                 2)))))
+    (math-midi-round midi)))
+
+(defun math-spn-to-midi (spn)
+  "Return the MIDI number corresponding to SPN."
+  (let* ((note (cdr (assoc (nth 1 (car spn)) math-notes)))
+         (octave (math-add (nth 2 (car spn)) 1))
+         (cents (nth 1 spn))
+         (midi  (math-add
+                 (math-mul 12 octave)
+                 note)))
+    (if cents
+        (math-add midi (math-div cents 100))
+      midi)))
+
+(defun math-midi-to-spn (midi)
+  "Return the scientific pitch notation corresponding to midi number MIDI."
+  (let (midin cents)
+    (if (math-integerp midi)
+        (setq midin midi 
+              cents nil)
+      (setq midin (math-floor midi)
+            cents (math-mul 100 (math-sub midi midin))))
+    (let* ((nr ;; This should be (math-idivmod midin 12), but with
+               ;; better behavior for negative midin.
+            (if (Math-negp midin)
+                (let ((dm (math-idivmod (math-neg midin) 12)))
+                  (if (= (cdr dm) 0)
+                      (cons (math-neg (car dm)) 0)
+                    (cons
+                     (math-sub (math-neg (car dm)) 1)
+                     (math-sub 12 (cdr dm)))))
+              (math-idivmod midin 12)))
+           (n (math-sub (car nr) 1))
+           (note (car (rassoc (cdr nr) math-notes))))
+      (if cents
+          (list '+ (list 'calcFunc-subscr note n) 
+                   (list '* cents '(var cents var-cents)))
+        (list 'calcFunc-subscr note n)))))
+
+(defun math-freq-to-spn (freq)
+  "Return the scientific pitch notation corresponding to FREQ Hz."
+  (math-with-extra-prec 3
+    (math-midi-to-spn (math-freq-to-midi freq))))
+
+(defun math-midi-to-freq (midi)
+  "Return the frequency of the note with midi number MIDI."
+  (list '*
+        (math-mul
+         440
+         (math-pow
+          2
+          (math-div 
+           (math-sub
+            midi
+            69)
+           12)))
+        '(var Hz var-Hz)))
+
+(defun math-spn-to-freq (spn)
+  "Return the frequency of the note with scientific pitch notation SPN."
+  (math-midi-to-freq (math-spn-to-midi spn)))
+
+(defun calcFunc-spn (expr)
+  "Return EXPR written as scientific pitch notation + cents."
+  ;; Get the coeffecient of Hz
+  (let (note)
+    (cond
+     ((setq note (math-freqp expr))
+      (math-freq-to-spn note))
+     ((setq note (math-midip expr))
+      (math-midi-to-spn note))
+     ((math-spnp expr)
+      expr)
+     (t
+      (math-reject-arg expr "*Improper expression")))))
+
+(defun calcFunc-midi (expr)
+  "Return EXPR written as a MIDI number."
+  (let (note)
+    (cond
+     ((setq note (math-freqp expr))
+      (math-freq-to-midi note))
+     ((setq note (math-spnp expr))
+      (math-spn-to-midi note))
+     ((math-midip expr)
+      expr)
+     (t
+      (math-reject-arg expr "*Improper expression")))))
+
+(defun calcFunc-freq (expr)
+  "Return the frequency corresponding to EXPR."
+  (let (note)
+    (cond
+     ((setq note (math-midip expr))
+      (math-midi-to-freq note))
+     ((setq note (math-spnp expr))
+      (math-spn-to-freq note))
+     ((math-freqp expr)
+      expr)
+     (t
+      (math-reject-arg expr "*Improper expression")))))
+
+(defun calc-freq (arg)
+  "Return the frequency corresponding to the expression on the stack."
+  (interactive "P")
+  (calc-slow-wrapper
+   (calc-unary-op "freq" 'calcFunc-freq arg)))
+
+(defun calc-midi (arg)
+  "Return the MIDI number corresponding to the expression on the stack."
+  (interactive "P")
+  (calc-slow-wrapper
+   (calc-unary-op "midi" 'calcFunc-midi arg)))
+
+(defun calc-spn (arg)
+  "Return the scientific pitch notation corresponding to the expression on the 
stack."
+  (interactive "P")
+  (calc-slow-wrapper
+   (calc-unary-op "spn" 'calcFunc-spn arg)))
+
+
 (provide 'calc-units)
 
 ;; Local variables:

=== modified file 'lisp/calc/calc.el'
--- a/lisp/calc/calc.el 2011-02-06 22:42:14 +0000
+++ b/lisp/calc/calc.el 2011-03-06 04:28:39 +0000
@@ -446,6 +446,11 @@
   :group 'calc
   :type '(string))
 
+(defcustom calc-note-threshold "1" 
+  "The number of cents that a frequency should be near a note
+to be identified as that note."
+  :type 'string
+  :group 'calc)
 
 (defface calc-nonselected-face
   '((t :inherit shadow       


reply via email to

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