1#!/bin/sh
2
3# SoX script: testtones.sh               (c) 2009 robs@users.sourceforge.net
4# Based on an original idea by Carsten Borchardt
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the
8# Free Software Foundation; either version 2 of the License, or (at your
9# option) any later version.
10#
11# This program is distributed in the hope that it will be useful, but
12# WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
14# Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20
21
22# Usage: testtones.sh [RATE [AUDIO_LENGTH]]
23#
24# This script generate files containing audio tones of various
25# frequencies and power levels that can be used to test an audio
26# system's reproduction quality and response.  By default, the
27# generated files are suitable for writing to a test CD (using a
28# programme such as k3b).
29#
30# The audio files are created in the current directory and a list of
31# the files generated is also created.  A description of each
32# generated file is sent to the console as the script executes.
33#
34# RATE (default 44100) is the sample-rate at which to generate the
35#   test tones.  Use 48000 to test a PC/work-station or DAT system.
36#
37# AUDIO_LENGTH (default 36) sets the length in seconds of (most of)
38#   the test-tones.
39#
40# Other parameters can be changed in the Configuration section below.
41
42
43
44# Configuration:
45
46sox="../src/sox"	# Where is sox?  E.g. sox, /usr/local/bin/sox
47type=wav		# File type, e.g. flac, cdda
48rate=44100		# Default sample rate
49#chans2="-c 2"		# Uncomment for all files to have 2 channels
50part_octave=6		# 5 for harmonic split, 6 for true half octave
51tone_length=36		# In seconds; not used for sweeps
52fade_time=.05		# Fade in/out time (for smooth start and end of tone)
53gain="gain -1"		# Headroom for playpack chain
54#NDD=-D			# Uncomment to disable TPDF default dither
55vol_dither="-f ge"	# Type of dither to be used for volume tests
56plot=yes		# Uncomment to enable PNG plots
57
58
59
60plot()
61{
62  [ -n "$plot" ] && [ -n "$name" ] && $sox -D "$name" -n \
63    spectrogram -h -w kaiser -o "`basename "$name" $type`png"
64}
65
66
67
68next_file()		# Generate incrementing file name; update variables
69{
70  plot  # the previous file
71
72  file_count=$(($file_count + 1))
73  name=`printf "%02i-%s.$type" $file_count "$1" | tr / "~"`
74  echo "  $name"
75  echo "$name" >> $log
76
77  length=$tone_length; [ -n "$2" ] && length=$2
78  total_length=$(($total_length + $length))
79
80  output="-b 16 $chans2 --comment="" $name"
81  fade="fade h $fade_time $length $fade_time"
82}
83
84
85
86# Initialisation:
87
88[ -n "$1" ] && rate="$1"
89[ -n "$2" ] && tone_length="$2"
90
91log="`basename "$0"`.log"
92: > $log					# Initialise log file
93
94total_length=0					# In seconds
95file_count=0
96
97freqs="100 1k 10k"
98input="$sox $NDD -r $rate -n"
99
100echo "Creating test audio files with sample-rate = $rate"
101
102
103
104echo; echo "Silence:"
105
106next_file silence
107$input -D $output trim 0 $length
108
109
110
111echo; echo "Noise colours:"
112
113next_file white-noise
114$input $output synth $length whitenoise $fade $gain
115next_file pink-noise
116$input $output synth $length pinknoise $fade $gain
117next_file brown-noise
118$input $output synth $length brownnoise $fade $gain
119
120
121
122echo; echo "Single, fixed frequency pure tones, half octave steps:"
123
124note=-60			# 5 octaves below middle A = 13.75Hz
125while [ $note -le 67 ]; do	# 5 and a bit octaves above middle A ~= 20kHz
126  if [ -x /usr/bin/bc ]; then
127    freq=`echo "scale = 9; 440 * e($note / 12 * l(2))" | bc -l`
128    next_file `printf "%.1f" $freq`Hz
129  else
130    next_file %$note
131  fi
132  $input $output synth -j 0 sine %$note $fade $gain
133  note=$(($note + $part_octave))
134  part_octave=$((12 - $part_octave))
135done
136
137
138
139echo; echo "Single, fixed frequency pure tones, decade/sub-decade steps:"
140
141for freq in 20 50 100 200 500 1k 2k 5k 10k 20k; do
142  next_file "${freq}hz"
143  $input $output synth sine $freq $fade $gain
144done
145
146
147
148echo; echo "Single, fixed frequency pure tones, CD frequency sub-harmonics:"
149
150for freq in 5512.5 11025 16537.5 22050; do
151  next_file "cd-${freq}hz"
152  $input $output synth $length sine $freq 0 25 $fade $gain
153done
154
155
156
157echo; echo "Sweep frequency @ 1 semitone/s, with a mark @ each octave of \`A':"
158
159phase1=75
160phase2=1
161for freq in %-60/%67 %67/%-60; do
162  next_file ${freq}_sweep 127
163  $input $output synth sine %30 \
164             synth sine amod 8.333333333333333333 0 $phase1 \
165             synth squa amod 0.08333333333333333333 0 $phase2 1 gain -9 \
166             synth $length sine mix $freq gain -h 3 $gain
167  phase1=41.66666666666666667
168  phase2=42.66666666666666667
169done
170
171
172
173echo; echo "Dual, fixed frequency pure tones:"
174
175next_file 9kHz+10kHz
176$input $output synth $length sine 9k synth sine mix 10k $fade $gain
177next_file 440hz+445hz
178$input $output synth $length sine 440 synth sine mix 445 $fade $gain
179
180
181
182echo; echo "Single, fixed frequency harmonic tones:"
183
184for freq in 100 1k 5k; do
185  for shape in square saw; do
186    next_file ${freq}hz_$shape
187    $input $output synth $length $shape $freq $fade gain -3
188  done
189done
190
191
192
193echo; echo "Single, fixed frequency pure tones, 12dB attenuation steps:"
194
195for freq in $freqs; do
196  for att in 12 24 36 48 60 72 84 96 108; do
197    next_file ${freq}hz_${att}dB_att
198    $input $output synth $length sine $freq gain -h -$att $fade dither $vol_dither
199  done
200done
201
202
203
204echo; echo "Sweep volume @ 1dB/s, -108->0->-108dB, with a mark (-40dB) every 12dB:"
205
206for freq in $freqs; do
207  next_file ${freq}hz_108dB-sweep 216
208  $input $output synth $length sin $freq sin %30 \
209             synth squ amod 0 100 sine amod 8.333333333333333333 0 75 \
210             synth exp amod 0.00462962962962962963 0 0 50 54 \
211             squ amod 0.08333333333333333333 0 1 1 \
212             remix 1v.99,2v.01 dither $vol_dither
213done
214
215
216
217echo; echo "1kHz tone offset with 1Hz:"
218
219next_file offset_10%_square
220$input $output synth $length square 1 sine 1000 remix 1v0.05,2v0.95 gain -h 0 $fade $gain
221next_file offset_50%_sine
222$input $output synth $length sine 1 synth sine mix 1000 $fade $gain
223
224
225
226echo; echo "Silence on one channel, full volume on the other:"
227
228for freq in $freqs; do
229  next_file ${freq}hz_left-chan
230  $input $output synth $length sine $freq remix 1 0 gain -h 0 $fade $gain
231  next_file ${freq}hz_right-chan
232  $input $output synth $length sine $freq remix 0 1 gain -h 0 $fade $gain
233done
234
235
236
237echo; echo "Phase difference (24, 90, 180 degrees) between channels:"
238
239for freq in $freqs; do
240  for phase in 6.667 25 50; do
241    next_file ${freq}hz_phase-$phase
242    $input $output synth $length sine $freq sine $freq 0 $phase $fade $gain
243  done
244done
245
246
247
248echo; echo "Plucked scale:"
249
250options=
251overdrive="gain -3"
252for f in pluck pluck_dist; do
253  next_file $f 42
254  note=-29
255  :>tmp.s32
256
257  while [ $note -lt 17 ]; do
258    $input -t s32 - synth .4 pluck %$note $options >> tmp.s32
259    note=$(($note + 1))
260  done
261  $input -t s32 - synth 1.2 pluck %$note $options >> tmp.s32
262
263  while [ $note -gt -29 ]; do
264    $input -t s32 - synth .4 pluck %$note $options >> tmp.s32
265    note=$(($note - 1))
266  done
267  $input -t s32 - synth pluck %$note $options fade t 0 4 3.6 >> tmp.s32
268
269  $sox -r $rate -c 1 tmp.s32 $output $overdrive remix 1 1 reverb 30
270
271  rm -f tmp.s32
272  options="0 0 60 75 0"
273  overdrive="overdrive gain -10"
274done
275
276
277
278plot  # the last file
279minutes=$(($total_length / 60))
280seconds=$(($total_length - $minutes * 60))
281echo; echo "Sample-rate = $rate; # files created = $file_count; total audio length = `printf "%i:%02i" $minutes $seconds`"
282