1
+ #pragma once
2
+
3
+ #include " AudioConfig.h"
4
+ #include " AudioTimer/AudioTimer.h"
5
+ #include " AudioTools/AudioLogger.h"
6
+ #include " AudioTools/AudioOutput.h"
7
+ #include " AudioTools/Buffers.h"
8
+
9
+ namespace audio_tools {
10
+
11
+ /* *
12
+ * @brief R2R configuration
13
+ * @author Phil Schatzmann
14
+ * @copyright GPLv3
15
+ */
16
+
17
+ class R2RConfig : public AudioInfo {
18
+ public:
19
+ Vector<int > channel1_pins{0 };
20
+ Vector<int > channel2_pins{0 };
21
+ };
22
+
23
+ /* *
24
+ * @brief DRAFT implementation for Output to R2R DAC
25
+ * @ingroup io
26
+ * @author Phil Schatzmann
27
+ * @copyright GPLv3
28
+ */
29
+
30
+ class R2ROutput : public AudioOutput {
31
+ public:
32
+ R2RConfig defaultConfig () {
33
+ R2RConfig r;
34
+ return r;
35
+ }
36
+
37
+ bool begin (R2RConfig c) {
38
+ cfg = c;
39
+ rcfg = c;
40
+ return begin ();
41
+ }
42
+
43
+ bool begin () override {
44
+ if (cfg.channels == 0 || cfg.channels > 2 ) {
45
+ LOGE (" channels is %d" , cfg.channels );
46
+ return false ;
47
+ }
48
+ if (rcfg.channel1_pins .size () == 0 ) {
49
+ LOGE (" channel1_pins not defined" );
50
+ return false ;
51
+ }
52
+ if (cfg.channels == 2 &&
53
+ rcfg.channel2_pins .size () != rcfg.channel1_pins .size ()) {
54
+ LOGE (" channel2_pins not defined" );
55
+ return false ;
56
+ }
57
+ setupPins ();
58
+ timer.setCallbackParameter (this );
59
+ return timer.begin (r2r_timer_callback, cfg.sample_rate , HZ);
60
+ }
61
+
62
+ size_t write (const uint8_t *data, size_t len) override {
63
+ size_t result = buffer.writeArray (data, len);
64
+ // activate output when buffer is full
65
+ if (!is_active && buffer.isFull ()) {
66
+ is_active = true ;
67
+ }
68
+ return result;
69
+ }
70
+
71
+ protected:
72
+ TimerAlarmRepeating timer;
73
+ NBuffer<uint8_t > buffer{1024 , 2 };
74
+ R2RConfig rcfg;
75
+
76
+ void setupPins () {
77
+ for (int j = 0 ; j < rcfg.channel1_pins .size (); j++) {
78
+ pinMode (rcfg.channel1_pins [j], OUTPUT);
79
+ }
80
+ for (int j = 0 ; j < rcfg.channel2_pins .size (); j++) {
81
+ pinMode (rcfg.channel2_pins [j], OUTPUT);
82
+ }
83
+ }
84
+
85
+ void writeValue (int channel) {
86
+ switch (cfg.bits_per_sample ) {
87
+ case 8 :
88
+ return writeValueT<int8_t >(channel);
89
+ case 16 :
90
+ return writeValueT<int16_t >(channel);
91
+ case 24 :
92
+ return writeValueT<int24_t >(channel);
93
+ case 32 :
94
+ return writeValueT<int32_t >(channel);
95
+ }
96
+ }
97
+
98
+ template <typename T>
99
+ void writeValueT (int channel) {
100
+ // don't do anything if we do not have enough data
101
+ if (buffer.available ()< sizeof (T)) return ;
102
+
103
+ // get next value from buffer
104
+ T value = 0 ;
105
+ buffer.readArray ((uint8_t *)&value, sizeof (T));
106
+ // convert to unsigned
107
+ unsigned uvalue = (int )value + NumberConverter::maxValueT<T>() + 1 ;
108
+ // scale value
109
+ uvalue = uvalue >> ((sizeof (T) * 8 ) - rcfg.channel1_pins .size ());
110
+ // output pins
111
+ switch (channel) {
112
+ case 0 :
113
+ for (int j = 0 ; j < rcfg.channel1_pins .size (); j++) {
114
+ digitalWrite (rcfg.channel1_pins [j], (uvalue >> j) & 1 );
115
+ }
116
+ break ;
117
+ case 1 :
118
+ for (int j = 0 ; j < rcfg.channel2_pins .size (); j++) {
119
+ digitalWrite (rcfg.channel2_pins [j], (uvalue >> j) & 1 );
120
+ }
121
+ break ;
122
+ }
123
+ }
124
+
125
+ static void r2r_timer_callback (void *ptr) {
126
+ R2ROutput *self = (R2ROutput *)ptr;
127
+ if (self->is_active ) {
128
+ // output channel 1
129
+ self->writeValue (0 );
130
+ // output channel 2
131
+ if (self->cfg .channels == 2 ) self->writeValue (1 );
132
+ };
133
+ }
134
+ };
135
+
136
+ } // namespace audio_tools
0 commit comments