Discussione:
BACH!
(troppo vecchio per rispondere)
Antonio Macchi
2009-11-14 16:54:16 UTC
Permalink
#!/bin/bash

dsp_fps=8000
volume=$'\xff'

duration=100

a0=220
cis=275
d=293
e=330
f=352
g=396
a=440
bes=469

createframe () { # $1=framelength
for (( i = 0; i < $1 - 1; ++i )); do
echo -n $'\x7f'
done
echo -n $volume
}

createnote () { # $1=freq $2=milliseconds
framelength=$(( dsp_fps / $1 ))
frame=`createframe $framelength`
realduration=$(( dsp_fps / 1000 * $2 ))
nframes=$(( realduration / framelength ))

for (( i = 0; i < $nframes; ++i )); do
echo -n $frame
done
}

score=( a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f a0 g
a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d )

for note in ${score[*]}; do
eval createnote \$$note $duration
done > /dev/dsp
Alessandro Selli
2009-11-14 22:52:44 UTC
Permalink
Post by Antonio Macchi
#!/bin/bash
Carino. Ho un paio di appunti da fare, però.
Post by Antonio Macchi
dsp_fps=8000
[...]
Post by Antonio Macchi
createnote () { # $1=freq $2=milliseconds
framelength=$(( dsp_fps / $1 ))
frame=`createframe $framelength`
realduration=$(( dsp_fps / 1000 * $2 ))
Qui si sta effettuando una divisione di una costante (dsp_fps) per una
costante (1000) dentro un ciclo (la funzione createnote è usata dentro
un costrutto for). Essendo il risultato ovviamente anche lui una
costante, questo è meglio calcolarlo fuori del ciclo una volta per
tutte. Ad esempio, si inizializza la costante dsp_fps_1000th=$((dsp_fps
/ 1000)) e si mette dentro la funzione realduration=$(( dsp_fps_1000th
* $2 )). L'unica buona ragione per mettere dentro la funzione anche la
divisione può essere quella di migliorare la precisione del calcolo
effettuando prima ma moltiplicazione di dsp_fps per $2 e dopo la
divisione per 1000, ma in questo caso specifico non si perde nulla per
il troncamento e comunque, così com'è scritto lo script, la divisione è
comunque effettuata prima della divisione.

Ad esempio:

echo $((20/3 * 2))
12

ma:

echo $((20 * 2 / 3))
13

che è un risultato più vicino a quello corretto di 13,3333.
Post by Antonio Macchi
score=( a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f a0 g
a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d )
for note in ${score[*]}; do
Non ha senso inizializzare una matrice se poi la si usa una volta sola
espandendola completamente estraendone tutti gli elementi. Si passa
allora ad usare direttamente la lista dei suoi elementi con:

for note in a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f
a0 g a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d
do ...

Si volesse veramente usare una matrice, si potrebbe fare in questo modo:

score=( a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f a0 g
a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d )
for ((i = 0; i < ${#score[@]}; ++i ))
do ...

e usare ${score[$i]} invece della variabile note.
Post by Antonio Macchi
eval createnote \$$note $duration
done > /dev/dsp
Anche lo eval è superfluo. note è una semplice variabile, si può
allora scrivere semplicemente createnote $note $duration, oppure, usando
la matrice come suggerito in alto, fare:

for ((i = 0; i < ${#score[@]}; ++i ))
do createnote ${score[$i]} $duration
done



Ciao,
--
Alessandro Selli http://alessandro.route-add.net
AVVERTENZA: i messaggi inviati a "trappola" non mi arriveranno.
WARNING: messages sent to "trappola" will never reach me.
Alessandro Selli
2009-11-15 00:22:42 UTC
Permalink
Alessandro Selli wrote:

[...]
Post by Alessandro Selli
divisione per 1000, ma in questo caso specifico non si perde nulla per
il troncamento e comunque, così com'è scritto lo script, la divisione è
comunque effettuata prima della divisione.
...comunque prima della *moltiplicazione*.
--
Alessandro Selli http://alessandro.route-add.net
AVVERTENZA: i messaggi inviati a "trappola" non mi arriveranno.
WARNING: messages sent to "trappola" will never reach me.
Alessandro Selli
2009-11-15 00:34:48 UTC
Permalink
Alessandro Selli wrote:

[...]
Post by Alessandro Selli
Anche lo eval è superfluo.
Opps, invece no: si sta estraendo il contenuto di una variabile il cui
nome è contenuto in un'altra variabile. Scusa, come non detto.


Ciao,
--
Alessandro Selli http://alessandro.route-add.net
AVVERTENZA: i messaggi inviati a "trappola" non mi arriveranno.
WARNING: messages sent to "trappola" will never reach me.
Antonio Macchi
2009-11-15 08:56:37 UTC
Permalink
Post by Alessandro Selli
[...]
Post by Alessandro Selli
Anche lo eval è superfluo.
Opps, invece no: si sta estraendo il contenuto di una variabile il cui
nome è contenuto in un'altra variabile. Scusa, come non detto.
eval e' un comandino parecchio potente e "attorcigliante"...
somiglia molto ai puntatori del c no?

ciao
Antonio Macchi
2009-11-15 08:54:18 UTC
Permalink
Post by Alessandro Selli
Post by Antonio Macchi
#!/bin/bash
Carino. Ho un paio di appunti da fare, però.
grazie
Post by Alessandro Selli
Post by Antonio Macchi
dsp_fps=8000
[...]
il frame rate di default del dispositivo
non e' detto che debba per forza essere una costante
non so come si faccia, ma credi sia possibile modificarlo
Post by Alessandro Selli
Post by Antonio Macchi
score=( a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f a0 g
a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d )
for note in ${score[*]}; do
Non ha senso inizializzare una matrice se poi la si usa una volta sola
espandendola completamente estraendone tutti gli elementi. Si passa
for note in a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f
a0 g a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d
la creazione della tabella vien fatta una volta sola
e' cosi anche la sua espansione
quindi una differenza in prestazioni molto piccola, compensata (imho) da
una maggior chiarezza del codice
Post by Alessandro Selli
Anche lo eval è superfluo. note è una semplice variabile, si può
allora scrivere semplicemente createnote $note $duration, oppure, usando
do createnote ${score[$i]} $duration
done
qui non ti seguo

guarda questo mini esempio

a=440
e=660
a2=880

for i in a e a2; do
echo $i
eval echo \$$i

echo createnote $i
eval echo createnote \$$i
done


sicuro di non aver preso un abbaglio?


grazie per l'interessamento
ciao
Alessandro Selli
2009-11-15 17:55:17 UTC
Permalink
Post by Antonio Macchi
Post by Alessandro Selli
Post by Antonio Macchi
#!/bin/bash
Carino. Ho un paio di appunti da fare, però.
grazie
Prego. Mi ricorda tanto i trucchi dei primi PC multimediali. In quei
tempi far "cantare" un PC non era cosa facile, e i maniaci dello
scripting facevano proprio giochetti come questo per far stupire gli amici.
Post by Antonio Macchi
Post by Alessandro Selli
Post by Antonio Macchi
dsp_fps=8000
[...]
il frame rate di default del dispositivo
non e' detto che debba per forza essere una costante
non so come si faccia, ma credi sia possibile modificarlo
Si potrebbe prevedere un parametro opzionale da passare allo script
sulla riga di comando, che se presente inizializzi questa stessa
variabile. Qualcosa come:

Err () {
echo "ERRORE: l'opzione '-f' prevede un parametro numerico" >&2
return 1
}

# Se ci sono parametri/switch sulla riga di comando leggili
while test $# -gt 0
do case "$1" in
'-f'|'--framerate') # Se il primo parametro è -f oppure --framerate
if test -n "$2" # deve essere seguito da un altro parametro
# che deve contenere solo caratteri numerici
then if test "$2" = $(echo $2 | tr -dc [0-9])
then # Si, sono solo caratteri numerici
dsp_fps=$2
# I primi due parametri sono stati riconosciuti e
# analizzati: possiamo cancellarli
shift 2
else # No, non è una stringa accettabile
Err
exit $?
fi
else # Non ci siamo, manca il parametro con il framerate
Err
# Proseguo lo stesso, ma userò il valore di dsp_fps di
# default (impostato all'inizio dello script)
# Butto il parametro
shift
fi ;;
*) echo "ERRORE: parametro \"$1\" sconosciuto" >&2
exit 2 ;;
esac
done

Di solito uso un costrutto case ... esac anche se ho un parametro solo
perché sarà poi facile estendere il tutto ad altri eventuali parametri
futuri.

Sono d'accordo che l'analisi dei parametri sulla riga di comando,
fatta per bene, complichi abbastanza le cose, ma fatta una volta si
ricicla n volte con n grande a piacere.
Se lo script prevedesse diversi valori specificabili sulla riga di
comando, ad esempio, si dovrebbe avere una funzione interna che scriva
una pagina di aiuto e inserire uno switch -h|--help|-? che esegua questa
funzione ed esca. E magari in caso di errore rilevato sulla riga di
comando eseguire questa funzione prima di uscire dallo script. Ma qui
stiamo veramente a fare le cose extralusso.
Post by Antonio Macchi
Post by Alessandro Selli
Post by Antonio Macchi
score=( a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f
a0 g
Post by Antonio Macchi
a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d )
for note in ${score[*]}; do
Non ha senso inizializzare una matrice se poi la si usa una volta sola
espandendola completamente estraendone tutti gli elementi. Si passa
for note in a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f
a0 g a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d
la creazione della tabella vien fatta una volta sola
e' cosi anche la sua espansione
quindi una differenza in prestazioni molto piccola, compensata (imho) da
una maggior chiarezza del codice
Allora usa una semplice variabile:

score='a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f a0 g
a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d'

for note in $score
...
Post by Antonio Macchi
sicuro di non aver preso un abbaglio?
A volte succede... :-)
Post by Antonio Macchi
grazie per l'interessamento
Prego, grazie a te per questo script divertente!


Ciao,
--
Alessandro Selli http://alessandro.route-add.net
AVVERTENZA: i messaggi inviati a "trappola" non mi arriveranno.
WARNING: messages sent to "trappola" will never reach me.
Antonio Macchi
2009-11-16 08:50:58 UTC
Permalink
Post by Alessandro Selli
Prego. Mi ricorda tanto i trucchi dei primi PC multimediali. In quei
tempi far "cantare" un PC non era cosa facile, e i maniaci dello
scripting facevano proprio giochetti come questo per far stupire gli amici.
a quei tempi bastava poco per stupire...
Post by Alessandro Selli
score='a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f a0 g
a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d'
for note in $score
...
cosi' ad occhio direi anch'io che usare una tabella sia piu' dispendioso
di usare una stringa (anche se sarebbe bello trovare uno script capace
di "dare prova tangibile" di questa affermazione...)


considerando poi che non ho mai in vita mia trovato un dev/dsp con
framerate doverso da 8000, lascio, per dovere di pulizia, lo script
"ripulito"

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

#!/bin/bash

dsp_fps=8000
one_millisecond=$(( $dsp_fps / 1000 ))

volume=$'\x70' # ff/00=mxavolume, 7f=mute
speed=70 # too fast?

a0=220
cis=275
d=293
e=330
f=352
g=396
a=440
bes=469

createframe () { # $1=framelength
for (( i = 0; i < $1 - 1; ++i )); do
echo -n $'\x7f'
done
echo -n $volume
}

createnote () { # $1=freq $2=milliseconds
framelength=$(( dsp_fps / $1 ))
frame=`createframe $framelength`
duration=$(( $one_millisecond * $2 ))
nframes=$(( duration / framelength ))

for (( i = 0; i < $nframes; ++i )); do
echo -n $frame
done
}

score="a0 a0 a0 d a0 e a0 f a0 d a0 e a0 f a0 g a0 e a0 f a0 g a0 a a0 f
a0 g a0 a a0 bes a0 g a0 a a0 f a0 g a0 e a0 f a0 d a0 e a0 cis a0 d d d d"

for note in $score; do
eval createnote \$$note $speed
done > /dev/dsp


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


adios!

Loading...