Use specific features of Arduino R4 boards to control the volume of a beep and play melodies while your program performs other functions.
Devices and components
Arduino®Nano R4
Transistor BC639
8Ω 0.5W speaker
Schottky diode 1N5819
Resistor 47 kΩ 1/4 W
Capacitor 10 µF 16 V
Software and tools
Arduino IDE
Project description
Introduction
Volume control
Software
Some additional information for those interested
Sound.ino
The toolbox
1/*******************************************/
2/* */
3/* SOUND */
4/* */
5/* Volume control on R4 board */
6/* */
7/*******************************************/
8
9#include <OPAMP.h>
10#include <Arduino_FreeRTOS.h>
11
12#define soundPin A6
13
14
15/*
16Connections :
17 A0 connected to A1
18 sound pin connected to A2 through a 47 kohms
19 resistor
20 A2 connected to A5 through a 47 kohms resistor
21 A3 connected to the base of a NPN transistor
22 collector of the NPN transistor connected to +5V
23 emitter of the NPN transistor connected to a 8 ohms
24 loudspeaker
25 other side of the loudspeaker connected to ground
26
27 optionally, to prevent any direct current to pass
28 through the transistor and the loudspeaker, a
29 10 microFarads capacitor can be inserted between
30 the pin A3 and the base of the transistor, and a
31 schottky diode can be connected between the base of
32 the transistor and the ground, the anode towards
33 the ground.
34*/
35
36/**********************************************/
37/****** ******/
38/****** FIRST PART: SOUND GENERATION ******/
39/****** WITH VOLUME CONTROL ******/
40/****** ******/
41/**********************************************/
42
43/*
44 The class soundClass replaces tone an noTone
45 functions while controlling the volume of the
46 produced sound.
47 playTone replaces tone with or without volume
48 control.
49 stopSound replaces noTone, you must use it and
50 never call noTone if you did not connect the
51 optional capacitor and schottky diode.
52 Examples of use of this class are given in the
53 file Examples.h.
54*/
55
56class soundClass
57{
58private:
59 const int nbVolGraduations=20; //0 excluded
60 int generalVolume;
61 int volume;
62 int pin;
63 int expGain(void);
64public:
65 soundClass(int pin);
66 ~soundClass();
67 void setGeneralVolume(int volume);
68 // 0 <= volume <= nbVolGraduations
69 void setVolume(int volume);
70 // 0 <= volume <= nbVolGraduations
71 void playTone(int freq);
72 void playTone(int freq, int vol);
73 // 0 <= vol <= nbVolGraduations
74 void stopSound(void);
75};
76
77/* The human ear does not have a linear response to
78 sound volume, but a logarithmic one. To compensate
79 for this, the volume control is given an
80 exponential response.
81 This is what the following function does after
82 combining the volume value with the general
83 volume. */
84
85int soundClass::expGain(void)
86{
87 const float curve=3; // Adjust to control the
88 // exponential response
89 const float minDAC=67; // Adjust to control output
90 // for 0 and 1 volume values
91
92 int trueVol=constrain(volume+
93 generalVolume-
94 nbVolGraduations,
95 0, nbVolGraduations);
96 return int(minDAC+(exp(trueVol*
97 curve/nbVolGraduations)-1)*
98 (511.-minDAC)/(exp(curve)-1));
99}
100
101soundClass::soundClass(int pin)
102{
103 this->pin=pin;
104 pinMode(pin, OUTPUT);
105 digitalWrite(pin, HIGH);
106 OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
107 analogWriteResolution(10);
108 generalVolume=nbVolGraduations;
109 volume=nbVolGraduations/2;
110}
111
112 soundClass::~soundClass()
113{
114 OPAMP.end();
115 pinMode(pin, INPUT);
116}
117
118/* The general volume is an int whose value must
119 be between 0 (no sound) and nbVolGraduations
120 (20). */
121
122void soundClass::setGeneralVolume(int vol)
123{
124 generalVolume=constrain(vol, 0, nbVolGraduations);
125 analogWrite(A0, expGain());
126}
127
128/* The volume is an int whose value must be between
129 0 (no sound) and nbVolGraduations (20). */
130
131void soundClass::setVolume(int vol)
132{
133 volume=constrain(vol, 0, nbVolGraduations);
134 analogWrite(A0, expGain());
135}
136
137void soundClass::playTone(int freq)
138{
139 tone(pin, freq);
140}
141
142void soundClass::playTone(int freq, int vol)
143{
144 setVolume(vol);
145 tone(pin, freq);
146}
147
148void soundClass::stopSound(void)
149{
150 noTone(pin);
151 digitalWrite(pin, HIGH);
152}
153
154soundClass sound(soundPin);
155
156#include "Examples.h"
157
158/***********************************************/
159/****** ******/
160/****** SECOND PART: PLAYING MELODIES ******/
161/****** IN REAL TIME ENVIRONMENT ******/
162/****** ******/
163/***********************************************/
164
165/* Play a melody while your program is running
166 other functions. The boolean 'playing' is true
167 while the melody is played. */
168
169TaskHandle_t loopHandle, playHandle;
170bool playing=false, continuous;
171int waitTime;
172
173/* Give names to your melodies and list them here */
174
175enum melodyNames{
176 simpleMelody,
177 beep,
178 BigBen
179 } melodyToPlay;
180
181/* List here the function to call to play each
182 melody */
183
184void (*melodyCalls[])(void)={
185 simpleMelodyCall,
186 beepCall,
187 BigBenCall
188};
189
190/* This function is called by the task which is
191 started each time you want to play a melody. */
192
193void playFunc(void *pvParameters)
194{
195 for(;;)
196 {
197 melodyCalls[melodyToPlay]();
198 if(!continuous)
199 {
200 playing=false;
201 vTaskDelete(NULL);
202 }
203 delay(waitTime);
204 }
205}
206
207/* Call this function to play a melody once */
208
209void startPlay(melodyNames melody)
210{
211 if(!playing)
212 {
213 melodyToPlay=melody;
214 continuous=false;
215 playing=true;
216 xTaskCreate(playFunc, "PlayFunc", 128,
217 NULL, 2, &playHandle);
218 }
219}
220
221/* Call this function to play a melody repeatedly.
222 You can add a parameter to specify the time in
223 ms between two repetitions. */
224
225void loopPlay(melodyNames melody,
226 int timeBeforeReplay=0)
227{
228 if(!playing)
229 {
230 melodyToPlay=melody;
231 continuous=true;
232 waitTime=timeBeforeReplay;
233 playing=true;
234 xTaskCreate(playFunc, "PlayFunc", 128,
235 NULL, 2, &playHandle);
236 }
237}
238
239/* Call this function to stop a melody. If the melody
240 was started by startPlay, it will stop
241 automatically when it is finished, but you can
242 stop it before the end.
243 If the melody was started by loopPlay, there is
244 no other way to stop it than stopPlay. */
245
246void stopPlay(void)
247{
248 if(playing)
249 vTaskDelete(playHandle);
250 sound.stopSound();
251 playing=false;
252}
253
254/* This function includes 'loop' in the real time
255 system, so you can use 'loop' as usual while
256 sound functions run in other tasks. */
257
258void loopThread(void *pvParameters)
259{
260 for(;;)
261 {
262 loop();
263 taskYIELD();
264 }
265}
266
267void setup()
268{
269 pinMode(LED_BUILTIN, OUTPUT);
270 xTaskCreate(loopThread, "loopThread", 128,
271 NULL, 1, &loopHandle);
272 vTaskStartScheduler();
273/* Nothing will be executed after this line */
274}
275
276void loop()
277{
278/* Choose the general volume. You can also control it
279 via a potentiometer whose value is read on an
280 analog pin. */
281
282 sound.setGeneralVolume(12);
283
284/* First example: play simpleMelody once. */
285
286 startPlay(simpleMelody);
287 while(playing)
288 {
289 /* Do what you want while the melody is playing.
290 For example, we make the builtin LED blink
291 at 1 Hz. */
292
293 digitalWrite(LED_BUILTIN,
294 !digitalRead(LED_BUILTIN));
295 delay(500);
296 }
297
298 digitalWrite(LED_BUILTIN, LOW);
299 delay(1000);
300
301/* Second example: play beep melody once, but stop it
302 before the end */
303
304 startPlay(beep);
305
306/* Builtin LED blinks at 2 Hz during 1 minute */
307 for(int i=0; i<120; i++)
308 {
309 digitalWrite(LED_BUILTIN,
310 !digitalRead(LED_BUILTIN));
311 delay(250);
312 }
313
314/* Stop the beeper before it stops automatically*/
315 stopPlay();
316 digitalWrite(LED_BUILTIN, LOW);
317 delay(2000);
318
319/* Third example : play BigBen malody repeatedly
320 and stop it after 30 s */
321
322 loopPlay(BigBen, 2000);
323/* Just wait, but you can do anything else */
324 delay(30000);
325 stopPlay();
326}
Examples.h
Must be in the same directory as sound.ino
1#include "pitches.h"
2
3/* First example : a simple melody */
4
5const int tempo=80;
6int sixtyFourth=60000/16/tempo; //duration of sixtyFourth note
7int pauseBetweenNotes=5;
8
9/*
10 Each note of the melody is defined by its frequency,
11 its duration in number of sixteenth notes,
12 and its initial volume.
13*/
14
15int notes[][3]={{NOTE_C5, 8, 16},
16 {NOTE_C5, 8, 14},
17 {NOTE_G5, 12, 16},
18 {NOTE_G5, 4, 14},
19 {NOTE_A5, 8, 16},
20 {NOTE_F5, 8, 14},
21 {NOTE_G5, 16, 16},
22
23 {NOTE_C5, 8, 12},
24 {NOTE_C5, 8, 10},
25 {NOTE_G5, 12, 12},
26 {NOTE_G5, 4, 10},
27 {NOTE_A5, 8, 12},
28 {NOTE_F5, 8, 10},
29 {NOTE_G5, 16, 12},
30
31 {NOTE_G5, 8, 16},
32 {NOTE_G5, 8, 14},
33 {NOTE_A5, 12, 16},
34 {NOTE_A5, 4, 14},
35 {NOTE_G5, 8, 16},
36 {NOTE_F5, 8, 14},
37 {NOTE_E5, 16, 16},
38
39 {NOTE_G5, 8, 12},
40 {NOTE_G5, 8, 10},
41 {NOTE_A5, 12, 12},
42 {NOTE_A5, 4, 10},
43 {NOTE_G5, 8, 12},
44 {NOTE_F5, 8, 10},
45 {NOTE_E5, 16, 12},
46
47 {NOTE_G5, 16, 16},
48 {NOTE_A5, 16, 17}};
49
50void simpleMelodyCall(void)
51{
52 for(int i=0; i<30; i++)
53 {
54 sound.playTone(notes[i][0], notes[i][2]);
55 delay(notes[i][1]*sixtyFourth);
56 sound.stopSound();
57 delay(pauseBetweenNotes);
58 }
59
60/* Start the last note */
61 sound.playTone(NOTE_C6, 18);
62 delay(500);
63
64/* Vibrato on the last note */
65 for(int i=0; i<5; i++)
66 {
67 sound.setVolume(17);
68 delay(30);
69 sound.setVolume(16);
70 delay(30);
71 sound.setVolume(17);
72 delay(30);
73 sound.setVolume(18);
74 delay(200);
75 }
76
77/* Fade out the last note */
78 for(int vol=18; vol>=0; vol--)
79 {
80 sound.setVolume(vol);
81 delay(15);
82 }
83
84 sound.stopSound();
85}
86
87/* Second example: beep with progressive
88 volume. Stops after 1 minute */
89
90const int beepFreq=2000;
91
92void beepBeep(void)
93{
94 sound.playTone(beepFreq);
95 delay(200);
96 sound.stopSound();
97 delay(100);
98 sound.playTone(beepFreq);
99 delay(200);
100 sound.stopSound();
101 delay(500);
102}
103
104void beepCall(void)
105{
106 for(int i=10; i<=20; i++)
107 {
108 sound.setVolume(i);
109 for(int j=0; j<2; j++)
110 beepBeep();
111 }
112 for(int j=0; j<50; j++)
113 beepBeep();
114}
115
116/* Third example : Big Ben */
117
118void bell(int freq)
119{
120 sound.playTone(freq, 20);
121 delay(10);
122 for(int i=20; i>7; i--)
123 {
124 sound.setVolume(i);
125 delay(50);
126 }
127 delay(300);
128 sound.stopSound();
129}
130
131void BigBenCall(void)
132{
133 bell(NOTE_E4);
134 bell(NOTE_C4);
135 bell(NOTE_D4);
136 bell(NOTE_G3);
137 delay(500);
138 bell(NOTE_G3);
139 bell(NOTE_D4);
140 bell(NOTE_E4);
141 bell(NOTE_C4);
142}
pitches.h
Must be in the same directory as sound.ino
1/*************************************************
2
3 * Public Constants
4
5 *************************************************/
6
7#define NOTE_B0 31
8#define NOTE_C1 33
9#define NOTE_CS1 35
10#define NOTE_D1 37
11#define NOTE_DS1 39
12#define NOTE_E1 41
13#define NOTE_F1 44
14#define NOTE_FS1 46
15#define NOTE_G1 49
16#define NOTE_GS1 52
17#define NOTE_A1 55
18#define NOTE_AS1 58
19#define NOTE_B1 62
20#define NOTE_C2 65
21#define NOTE_CS2 69
22#define NOTE_D2 73
23#define NOTE_DS2 78
24#define NOTE_E2 82
25#define NOTE_F2 87
26#define NOTE_FS2 93
27#define NOTE_G2 98
28#define NOTE_GS2 104
29#define NOTE_A2 110
30#define NOTE_AS2 117
31#define NOTE_B2 123
32#define NOTE_C3 131
33#define NOTE_CS3 139
34#define NOTE_D3 147
35#define NOTE_DS3 156
36#define NOTE_E3 165
37#define NOTE_F3 175
38#define NOTE_FS3 185
39#define NOTE_G3 196
40#define NOTE_GS3 208
41#define NOTE_A3 220
42#define NOTE_AS3 233
43#define NOTE_B3 247
44#define NOTE_C4 262
45#define NOTE_CS4 277
46#define NOTE_D4 294
47#define NOTE_DS4 311
48#define NOTE_E4 330
49#define NOTE_F4 349
50#define NOTE_FS4 370
51#define NOTE_G4 392
52#define NOTE_GS4 415
53#define NOTE_A4 440
54#define NOTE_AS4 466
55#define NOTE_B4 494
56#define NOTE_C5 523
57#define NOTE_CS5 554
58#define NOTE_D5 587
59#define NOTE_DS5 622
60#define NOTE_E5 659
61#define NOTE_F5 698
62#define NOTE_FS5 740
63#define NOTE_G5 784
64#define NOTE_GS5 831
65#define NOTE_A5 880
66#define NOTE_AS5 932
67#define NOTE_B5 988
68#define NOTE_C6 1047
69#define NOTE_CS6 1109
70#define NOTE_D6 1175
71#define NOTE_DS6 1245
72#define NOTE_E6 1319
73#define NOTE_F6 1397
74#define NOTE_FS6 1480
75#define NOTE_G6 1568
76#define NOTE_GS6 1661
77#define NOTE_A6 1760
78#define NOTE_AS6 1865
79#define NOTE_B6 1976
80#define NOTE_C7 2093
81#define NOTE_CS7 2217
82#define NOTE_D7 2349
83#define NOTE_DS7 2489
84#define NOTE_E7 2637
85#define NOTE_F7 2794
86#define NOTE_FS7 2960
87#define NOTE_G7 3136
88#define NOTE_GS7 3322
89#define NOTE_A7 3520
90#define NOTE_AS7 3729
91#define NOTE_B7 3951
92#define NOTE_C8 4186
93#define NOTE_CS8 4435
94#define NOTE_D8 4699
95#define NOTE_DS8 4978
Volume_test.ino
Exponential Response Adjustment Tool
1/*******************************************/
2/* */
3/* Volume test */
4/* */
5/* Adjust the exponential correction */
6/* */
7/*******************************************/
8
9#include <OPAMP.h>
10
11#define soundPin A6
12
13
14class soundClass
15{
16private:
17 const int nbVolGraduations=20; //0 excluded
18 int generalVolume;
19 int volume;
20 int pin;
21 int expGain(void);
22public:
23 soundClass(int pin);
24 ~soundClass();
25 void setGeneralVolume(int volume);
26 // 0 <= volume <= nbVolGraduations
27 void setVolume(int volume);
28 // 0 <= volume <= nbVolGraduations
29 void playTone(int freq);
30 void playTone(int freq, int vol);
31 // 0 <= vol <= nbVolGraduations
32 void stopSound(void);
33};
34
35/* The human ear does not have a linear response to
36 sound volume, but a logarithmic one. To compensate
37 for this, the volume control is given an
38 exponential response.
39 This is what the following function does after
40 combining the volume value with the general
41 volume. */
42
43int soundClass::expGain(void)
44{
45 const float curve=3; // Adjust to control the
46 // exponential response
47 const float minDAC=67; // Adjust to control output
48 // for 0 and 1 volume values
49
50 int trueVol=constrain(volume+
51 generalVolume-
52 nbVolGraduations,
53 0, nbVolGraduations);
54 return int(minDAC+(exp(trueVol*
55 curve/nbVolGraduations)-1)*
56 (511.-minDAC)/(exp(curve)-1));
57}
58
59soundClass::soundClass(int pin)
60{
61 this->pin=pin;
62 pinMode(pin, OUTPUT);
63 digitalWrite(pin, HIGH);
64 OPAMP.begin(OPAMP_SPEED_HIGHSPEED);
65 analogWriteResolution(10);
66 generalVolume=nbVolGraduations;
67 volume=nbVolGraduations/2;
68}
69
70 soundClass::~soundClass()
71{
72 OPAMP.end();
73 pinMode(pin, INPUT);
74}
75
76/* The general volume is an int whose value must
77 be between 0 (no sound) and nbVolGraduations
78 (20). */
79
80void soundClass::setGeneralVolume(int vol)
81{
82 generalVolume=constrain(vol, 0, nbVolGraduations);
83 analogWrite(A0, expGain());
84}
85
86/* The volume is an int whose value must be between
87 0 (no sound) and nbVolGraduations (20). */
88
89void soundClass::setVolume(int vol)
90{
91 volume=constrain(vol, 0, nbVolGraduations);
92 analogWrite(A0, expGain());
93 Serial.println(expGain());
94}
95
96void soundClass::playTone(int freq)
97{
98 tone(pin, freq);
99}
100
101void soundClass::playTone(int freq, int vol)
102{
103 setVolume(vol);
104 tone(pin, freq);
105}
106
107void soundClass::stopSound(void)
108{
109 noTone(pin);
110 digitalWrite(pin, HIGH);
111}
112
113soundClass sound(soundPin);
114
115void setup()
116{
117 Serial.begin(115200);
118 while(!Serial);
119 delay(100);
120 Serial.println("Volume | Exponential response");
121 Serial.print(" 0 | ");
122 sound.setGeneralVolume(100);
123 // You may have changed nbVolGraduations
124 sound.setVolume(0);
125 sound.playTone(1000);
126}
127
128int vol=0;
129byte in;
130void loop()
131{
132 if(Serial.available())
133 {
134 in=Serial.read();
135 if((in==0x0A) || (in==0x0D))
136 {
137 if(vol<10)
138 Serial.print(" ");
139 Serial.print(vol);
140 Serial.print(" | ");
141 sound.setVolume(vol);
142 vol=0;
143 }
144 else if((in>='0') && (in<='9'))
145 vol=10*vol+in-'0';
146 }
147}
Downloadable files
Sound control
Pseudo-gain controlled amplifier
Sound control.jpg
Sound Toolbox
Circuit for testing the sound toolbox
Sound Toolbox.jpg
Note: Content and images are from: https://projecthub.arduino.cc/, with some modifications.
If you want it removed due to copyright reasons, please leave a comment. Thank you.
I want to share this article more widely so that everyone knows about Arduino and your project.