Skip to content

Commit d057a0a

Browse files
committed
Network support
1 parent f0f4dd8 commit d057a0a

File tree

4 files changed

+246
-5
lines changed

4 files changed

+246
-5
lines changed

examples/streams-mozzi-a2dp/streams-mozzi-a2dp.ino

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ using namespace audio_tools;
2121

2222
typedef int16_t sound_t; // sound will be represented as int16_t (with 2 bytes)
2323
uint8_t channels = 2; // The stream will have 2 channels
24-
MozziGenerator mozzi; // subclass of SoundGenerator
24+
MozziGenerator mozzi(CONTROL_RATE); // subclass of SoundGenerator
2525
GeneratedSoundStream<sound_t> in(mozzi, channels); // Stream generated with mozzi
26-
A2DPStream out = A2DPStream::instance() ; // A2DP input - A2DPStream is a singleton!
26+
A2DPStream out = A2DPStream::instance() ; // A2DP output - A2DPStream is a singleton!
2727
StreamCopy copier(out, in); // copy in to out
2828

2929
/// Copied from AMsynth.ino
@@ -49,7 +49,7 @@ Q8n0 octave_start_note = 42;
4949
void setup(){
5050
Serial.begin(115200);
5151

52-
if (mozzi.info().sample_rate!=44100){
52+
if (mozzi.config().sample_rate!=44100){
5353
Serial.println("Please set the AUDIO_RATE in the mozzi_config.h to 44100");
5454
stop();
5555
}
@@ -62,7 +62,7 @@ void setup(){
6262
kNoteChangeDelay.set(200); // note duration ms, within resolution of CONTROL_RATE
6363
aModDepth.setFreq(13.f); // vary mod depth to highlight am effects
6464
randSeed(); // reseed the random generator for different results each time the sketch runs
65-
mozzi.begin(CONTROL_RATE);
65+
in.begin();
6666
}
6767

6868
void updateControl(){
@@ -110,7 +110,7 @@ void updateControl(){
110110
}
111111

112112
AudioOutput_t updateAudio(){
113-
long mod = (128u+ aModulator.next()) * ((byte)128+ aModDepth.next());
113+
int32_t mod = (128u+ aModulator.next()) * ((byte)128+ aModDepth.next());
114114
return MonoOutput::fromNBit(24, mod * aCarrier.next());
115115
}
116116

sandbox/streams-mozzi-udp/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Using Mozzi with UDP
2+
3+
I am providing a simple integration for [Mozzi](https://sensorium.github.io/Mozzi/). Unfortunatly I needed to make some small changes to make things work together. Those can be found in my [fork](https://github.com/pschatzmann/Mozzi).
4+
5+
We can send the output via TCP/IP or UDP. In this example I am using UDP:
6+
7+
....
8+
9+
UDPStream out; // UDP Output - A2DPStream is a singleton!
10+
11+
// connect to WIFI
12+
WiFi.begin("wifi", "password");
13+
while (WiFi.status() != WL_CONNECTED){
14+
Serial.print(".");
15+
delay(500);
16+
}
17+
18+
// We send the sound via UDP
19+
IPAddress ip(192, 168, 1, 255);
20+
out.begin(ip, 9999);
21+
....
22+
23+
24+
25+
On the other side we can use netcat to play the sound
26+
....
27+
nc -u 192.168.0.255 9999|play –c 1 –b 16 –e signed –t raw –r 44k -
28+
....
29+
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/**
2+
* @file streams-generator-a2dp.ino
3+
* @author Phil Schatzmann
4+
* @brief see https://github.com/pschatzmann/arduino-audio-tools/blob/main/examples/streams-generator-a2dp/README.md
5+
*
6+
* @author Phil Schatzmann
7+
* @copyright GPLv3
8+
*
9+
*/
10+
#include "AudioTools.h"
11+
#include "AudioNet.h"
12+
#include "AudioMozzi.h"
13+
#include "WiFi.h"
14+
#include <Oscil.h>
15+
#include <tables/cos2048_int8.h> // table for Oscils to play
16+
#include <mozzi_fixmath.h>
17+
#include <EventDelay.h>
18+
#include <mozzi_rand.h>
19+
#include <mozzi_midi.h>
20+
21+
using namespace audio_tools;
22+
23+
typedef int16_t sound_t; // sound will be represented as int16_t (with 2 bytes)
24+
uint8_t channels = 2; // The stream will have 2 channels
25+
MozziGenerator mozzi(CONTROL_RATE); // subclass of SoundGenerator
26+
GeneratedSoundStream<sound_t> in(mozzi, channels); // Stream generated with mozzi
27+
UDPStream out; // UDP Output - A2DPStream is a singleton!
28+
StreamCopy copier(out, in); // copy in to out
29+
30+
/// Copied from AMsynth.ino
31+
#define CONTROL_RATE 64 // Hz, powers of 2 are most reliable
32+
33+
// audio oscils
34+
Oscil<COS2048_NUM_CELLS, AUDIO_RATE> aCarrier(COS2048_DATA);
35+
Oscil<COS2048_NUM_CELLS, AUDIO_RATE> aModulator(COS2048_DATA);
36+
Oscil<COS2048_NUM_CELLS, AUDIO_RATE> aModDepth(COS2048_DATA);
37+
38+
// for scheduling note changes in updateControl()
39+
EventDelay kNoteChangeDelay;
40+
41+
// synthesis parameters in fixed point formats
42+
Q8n8 ratio; // unsigned int with 8 integer bits and 8 fractional bits
43+
Q24n8 carrier_freq; // unsigned long with 24 integer bits and 8 fractional bits
44+
Q24n8 mod_freq; // unsigned long with 24 integer bits and 8 fractional bits
45+
46+
// for random notes
47+
Q8n0 octave_start_note = 42;
48+
49+
50+
void setup(){
51+
Serial.begin(115200);
52+
53+
// connect to WIFI
54+
WiFi.begin("network", "password");
55+
while (WiFi.status() != WL_CONNECTED){
56+
Serial.print(".");
57+
delay(500);
58+
}
59+
Serial.println("Connected to WIFI");
60+
61+
if (mozzi.config().sample_rate!=44100){
62+
Serial.println("Please set the AUDIO_RATE in the mozzi_config.h to 44100");
63+
stop();
64+
}
65+
66+
// We send the sound via UDP (to a broadcast address)
67+
IPAddress ip(192, 168, 1, 255);
68+
out.begin(ip, 3333);
69+
70+
ratio = float_to_Q8n8(3.0f); // define modulation ratio in float and convert to fixed-point
71+
kNoteChangeDelay.set(200); // note duration ms, within resolution of CONTROL_RATE
72+
aModDepth.setFreq(13.f); // vary mod depth to highlight am effects
73+
randSeed(); // reseed the random generator for different results each time the sketch runs
74+
in.begin();
75+
}
76+
77+
void updateControl(){
78+
static Q16n16 last_note = octave_start_note;
79+
80+
if(kNoteChangeDelay.ready()){
81+
82+
// change octave now and then
83+
if(rand((byte)5)==0){
84+
last_note = 36+(rand((byte)6)*12);
85+
}
86+
87+
// change step up or down a semitone occasionally
88+
if(rand((byte)13)==0){
89+
last_note += 1-rand((byte)3);
90+
}
91+
92+
// change modulation ratio now and then
93+
if(rand((byte)5)==0){
94+
ratio = ((Q8n8) 1+ rand((byte)5)) <<8;
95+
}
96+
97+
// sometimes add a fractionto the ratio
98+
if(rand((byte)5)==0){
99+
ratio += rand((byte)255);
100+
}
101+
102+
// step up or down 3 semitones (or 0)
103+
last_note += 3 * (1-rand((byte)3));
104+
105+
// convert midi to frequency
106+
Q16n16 midi_note = Q8n0_to_Q16n16(last_note);
107+
carrier_freq = Q16n16_to_Q24n8(Q16n16_mtof(midi_note));
108+
109+
// calculate modulation frequency to stay in ratio with carrier
110+
mod_freq = (carrier_freq * ratio)>>8; // (Q24n8 Q8n8) >> 8 = Q24n8
111+
112+
// set frequencies of the oscillators
113+
aCarrier.setFreq_Q24n8(carrier_freq);
114+
aModulator.setFreq_Q24n8(mod_freq);
115+
116+
// reset the note scheduler
117+
kNoteChangeDelay.start();
118+
}
119+
}
120+
121+
AudioOutput_t updateAudio(){
122+
int32_t mod = (128u+ aModulator.next()) * ((byte)128+ aModDepth.next());
123+
return MonoOutput::fromNBit(24, mod * aCarrier.next());
124+
}
125+
126+
// Arduino loop
127+
void loop() {
128+
if (out)
129+
copier.copy();
130+
}

src/AudioNet.h

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#pragma once
2+
3+
#include <WiFi.h>
4+
#include <WiFiUdp.h>
5+
6+
namespace audio_tools {
7+
8+
/**
9+
* @brief Just an alternative name for EthernetClient - To be consistent with the other Stream classes we support begin and end on top of the
10+
* standard connect and end methods.
11+
*
12+
*/
13+
14+
class IPStream : public WiFiClient {
15+
public:
16+
uint8_t begin(IPAddress remote_host, uint16_t port) {
17+
active = connect(remote_host, port);
18+
return active;
19+
}
20+
21+
virtual void end() {
22+
stop();
23+
active = false;
24+
}
25+
26+
operator bool() {
27+
return active;
28+
}
29+
30+
protected:
31+
bool active;
32+
33+
34+
};
35+
36+
/**
37+
* @brief The same as EthernetUDP with the difference that we indicate the destination host in the begin and not
38+
* for each packet write
39+
*/
40+
class UDPStream : public WiFiUDP {
41+
public:
42+
43+
virtual uint8_t begin(IPAddress remote_host, uint16_t remote_port, uint16_t local_port=0, bool multicast = false ) {
44+
ESP_LOGI(AUDIO_TAG, "begin");
45+
this->remote_host = remote_host;
46+
this->remote_port = remote_port;
47+
this->local_port = local_port != 0 ? local_port : remote_port;
48+
49+
if (multicast){
50+
active = WiFiUDP::beginMulticast(remote_host, remote_port);
51+
} else {
52+
active = WiFiUDP::begin(local_port);
53+
}
54+
55+
return active;
56+
}
57+
58+
virtual void end() {
59+
WiFiUDP::stop();
60+
active = false;
61+
}
62+
63+
virtual size_t write(const uint8_t *buffer, size_t size) {
64+
ESP_LOGD(AUDIO_TAG, "write %u bytes", size);
65+
beginPacket(remote_host, remote_port);
66+
size_t result = WiFiUDP::write(buffer, size);
67+
endPacket();
68+
return result;
69+
}
70+
71+
operator bool() {
72+
return active;
73+
}
74+
75+
protected:
76+
IPAddress remote_host;
77+
uint16_t remote_port;
78+
uint16_t local_port;
79+
bool active;
80+
};
81+
82+
}

0 commit comments

Comments
 (0)