Open Source Audio Platform For Embedded Systems
Open Source Audio Platform For Embedded Systems
KIEL
M ASTER T HESIS
Author: Examiner:
Yasmeen S ULTANA Prof. Dr. Robert M ANZKE
in Informaton Technology
May 1, 2018
1
Declaration of Authorship
I, Yasmeen S ULTANA, declare that this thesis titled, “Open-Source Audio Platform for
Embedded Systems” and the work presented in it are my own. I confirm that:
• This work was done wholly or mainly while in candidature for a research degree
at this University.
• Where any part of this thesis has previously been submitted for a degree or any
other qualification at this University or any other institution, this has been clearly
stated.
• Where I have consulted the published work of others, this is always clearly at-
tributed.
• Where I have quoted from the work of others, the source is always given. With
the exception of such quotations, this thesis is entirely my own work.
Signed:
Date:
2
Abstract
The CTAG face 2|4 sound card is a hardware platform developed at Kiel University
of Applied Sciences for all audio applications. This board is based on Analog devices
AD1938 audio codec which contains 4 input ADC channels and 8 DAC output chan-
nels. The Linux based audio driver for this board has previously been developed on
open source development platforms like the beagle bone and raspberry pi.
In this thesis, a further audio driver for the Teensyduino embedded platform has
been developed using the open source Teensy audio library. At the time of this writ-
ing, the Teensy audio library offered two stereo I2S input / output channels, however,
the sound card offers four stereo I2S audio outputs channels and two stereo inputs I2S
channels. The AD1938 codec uses TDM encoding and decoding for transmitting and
receiving multiple channels on a serial line. The development of the new audio driver
includes the configuration of AD1938 codec via SPI driver and configuring the Teensy
3.6 micro controller registers to support I2S TDM streaming.
The Audio driver has been developed and tested to receive 8 channel input data
and transmits 4 channel output data at 48kHz sample rate. By connecting two CTAG
sound cards in daisy chain mode with a Teensy, the system can be expanded to 8 input
channels and 16 output channels. The audio driver has also been extended to support
these additional channels as well as supporting both master and slave mode.
The second part of the thesis deals with adding a reverberation effect for audio
received from the audio codec. The Schroeder-Moorer based freeverb algorithm devel-
oped by Jezar is an often used reverb in open source platforms. The freeverb algorithm
is suitable for the embedded system because of its small memory footprint as the decay
time is mostly influenced by algorithmic parameters. A fixed-point implementation
of the freeverb algorithm has been developed for the Teensy audio library for stereo
channels and is tested in real-time.
3
Acknowledgements
I would like to express my special gratitude to my professor Dr. Robert Manzke, who
motivated and introduced me to the embedded system world. Special thanks for his
continuous advice, encouragement and technical support throughout the course of this
thesis. Furthermore, technical equipment, such as oscilloscope, and facilities were pro-
vided in the University of Applied Sciences Kiel. Thank you very much. Also, I would
like to thank Mr. Henrik Langer, co-student who was helpful with the information re-
garding daisy chain connections. I must thank my two sweet daughters, Nabila and
Inaaya for their splendid support and for standing with me during my studies with
patience and tolerance.
4
Contents
Declaration of Authorship 1
Abstract 2
Acknowledgements 3
List of Figures 6
List of Tables 8
1 Introduction 10
1.1 Open source Eco-System . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.2 Teensy Eco-System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3 Scope of Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2 Technical Background 18
2.1 Pulse-Code-Modulation (PCM) . . . . . . . . . . . . . . . . . . . . . . . . 18
2.2 Inter-IC Sound (I 2 S) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3 Time Division Multiplexed (TDM) Mode . . . . . . . . . . . . . . . . . . 18
2.4 Serial Peripheral Interface (SPI) . . . . . . . . . . . . . . . . . . . . . . . . 20
2.5 Direct Memory Access(DMA) . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.6 CTAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.7 AD1938 Codec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.8 Teensy 3.6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.9 Audio Library Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.10 DMA on K66144M180FS . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.11 Daisy chain . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.12 Digital signal processing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.13 Reverberation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.14 Literature Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
4 Evaluation 73
4.1 Latency . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.2 Buffer Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
4.3 DSP Benchmark using CPU . . . . . . . . . . . . . . . . . . . . . . . . . . 75
4.4 Benefits of DMA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.5 Memory foot-print . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.6 Power Consumption . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5 Discussion / Limitations 78
5.1 Discuss results and findings . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.2 what could not be achieved . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6 Conclusion 81
6.1 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
A Appendix 86
6
List of Figures
2.1 An Electron . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.2 Generic TDM Interface [13, S.1, Figure 1] . . . . . . . . . . . . . . . . . . . 20
2.3 4-wire SPI bus configuration with multiple slaves . . . . . . . . . . . . . 21
2.4 SPI bus timing [14, Figure 2] . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.5 Functional Block Diagram [19, Figure 1] . . . . . . . . . . . . . . . . . . . 24
2.6 Teensy 3.6 pin configuration . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.7 Clocking diagram [21, Figure 6-1] . . . . . . . . . . . . . . . . . . . . . . . 27
2.8 I 2 S/SAI clock generation [21, Figure 6-12] . . . . . . . . . . . . . . . . . . 28
2.9 Teensy Software Architecture . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.10 eDMA module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.11 Nested Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.12 Three Devices in Daisy Chain . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.13 Discrete-time FIR filter of order N [28] . . . . . . . . . . . . . . . . . . . . 34
2.14 Sample IIR filter structure . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.15 Feed Forward comb filter . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.16 Feedback comb filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.17 All-pass filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.18 Sound wave traveling back and forth in a closed-space environment [34,
Figure 1.2.1] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.19 The impulse responses of a large hall and cathedral. [35, Figure 11.1] . . 41
2.20 Schroeder’s original reverb design . . . . . . . . . . . . . . . . . . . . . . 42
2.21 Moorer’s reverb design . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.22 Low pass comb filter design . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.23 Freeverb algorithm Model [42] . . . . . . . . . . . . . . . . . . . . . . . . 45
2.24 Freeverb algorithm for mono channel [41] . . . . . . . . . . . . . . . . . 45
2.25 Lowpass comb filter in freeverb . . . . . . . . . . . . . . . . . . . . . . . 46
List of Tables
1.1 Teensy 3.6 Open source Operating system and their licenses . . . . . . . 11
1.2 Packages in Teensy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.3 Audio Connections and Memory . . . . . . . . . . . . . . . . . . . . . . . 16
3.1 Pin connections of Master and Slave AD1938 AudioCard and Teensy 3.6 50
3.2 Daisy chain Pin connections between Master and Slave AD1938 codec . 51
3.3 ADC daisy chain configuration . . . . . . . . . . . . . . . . . . . . . . . . 53
3.4 DAC daisy chain configuration . . . . . . . . . . . . . . . . . . . . . . . . 53
List of Abbreviations
Chapter 1
Introduction
Open source Initiative (OSI) published a document that describes the open source
definition. The terms mentioned by OSI are [2] free redistribution, the author or license
holder cannot collect royalty on the distribution of program, this program must make
available to all the use , license should allow changes and derived works and must al-
low them to distribute under same terms as the original license, no person or group
must be denied to access the code, License must not specific to a product, licensed soft-
ware cannot restrict other software, license must be technology neutral and software
must follow any non restricted licenses standards (like GNU General Public License
(GPL), Apache Software License, Intel Open Source License, Berkeley Software Distri-
bution (BSD) license etc).
The main purpose behind this approach is that a large group of people collabora-
tively without any financial gain wants to produce more useful and bug free product
for everyone to use. Open source software (OSS) supporters argue that this kind of
approach is faster and efficient and results are better quality. Now a days, even com-
mercial software industries are also attracted to this approach. The Internet has brought
students, hobbyist, developers, and users across the world together as a community to
exchange ideas, discuss the problems and provide the solutions .
Due to a layered software architecture, drivers and middle wares libraries are hid-
ing the hardware details and their configuration details; any user without hardware
knowledge can develop an application using open source hardware platform and can
contribute to open source community.
Chapter 1. Introduction 11
The open source software(OSS) movement started by academicians like Donald Knuth,
Richard Stallman against the commercialization of software has gained momentum in
early 80’s and led to the development of open source GNU operating system. With co-
operation from developers, collaborating over the internet, Linus Torvalds as a student
released the first version of the Linux kernel. The merger of GNU operating system
with Linux kernel formed complete open source operating system. The University of
California, Berkeley developed a UNIX derivative operating system and distributed as
Berkeley Software Distribution(BSD).
The table contains few open source operating system and their licenses
TABLE 1.1: Teensy 3.6 Open source Operating system and their licenses
The high-speed CPU processors and memory chips availability at a lower price has en-
couraged the companies to develop open source hardware platforms like Beagle bone,
Raspberry pi for Linux operating system.The following are the mostly used open source
hardware platforms by the student community.
Chapter 1. Introduction 12
1. BeagleBoard: Texas Instrument along with the electronics parts distributors like
Digi-key and element14 has produced a low power open source single board com-
puter. The primary goal of this educational board is promoting open source hard-
ware in colleges and universities. This board has Texas Instrument’s OMAP3530
system-on-a-chip(SoC) which, includes an ARM Cortex-A8 CPU(720 MHz), Imag-
ination Technologies PowerVR SGX 2D/3D graphics processor and TMS320C64x+
core (520 MHz). This board can also run on different operating systems like Linux,
Ubuntu, Android and Windows CE. The Beagle board has been updated with
CPU, memory, and peripherals over the time. The Beagle board versions are
BeagleBoard-xM, BeagleBone, BeagleBone Black, and BeagleBoard-X15 respec-
tively.
3. Arduino Yún: The Arduino Yún is a microcontroller board based on the MIPS32
24K and ATmega32U4 CPU and the Atheros AR9331 processor with 400MHz
speed and 64MB (AR9331) and 2.5KB (ATmega) memory. The Atheros proces-
sor supports a Linux distribution based on OpenWrt named Linino OS [4]. It
contains features like Ethernet and WiFi support, USB Port, MicroSD port for ex-
ternal storage, 20 Input/Output pins. This is a type of Arduino board but with an
ability to communicate with the Linux on board thus Arduni Yún offers powerful
network mini computer.
4. Inetl Galileo: It is the first Arduino- boards based on Intel x86 architecture. This
board combines the best of both Linux and an arduino emulator. Galileo is a mi-
crocontroller board based on the Intel
R Quark SoC X1000 Application Processor,
a 32-bit Intel Pentium-class system on a chip. [5]. Galileo is truely an open source,
as schematic diagram and source code is available online and can be downloaded
without any software license.
Chapter 1. Introduction 13
There are open source smaller microcontroller hardware platforms like Arduino based
boards and Teensy for new generation sensors, wearable, low power applications which
don’t need higher memory and have fast booting requirement.
Key features[9]
Name Description
framework-arduinoteensy Arduino Wiring-based Framework
framework-mbed mbed Framework
tool-teensy Teensy Loader
toolchain-atmelavr avr-gcc
toolchain-gccarmnoneeabi gcc-arm-embedded
History of Teensy
Teensy has developed many powerful micro controller functions in the open source
environment. The teensy’s hardware and advance libraries of Arduino can be used to
develop more powerful features. Similar to any other platform, teensy has also went
through the iteration and now it is up to the 3rd generation of teensy board.
Teensy 1.0: It was introduced in late 2008 and was the first Arduino compatible
board developed with USB communication feature. It offers 12 Mbps native USB.
Teensy 2.x: The Teensy 2.0 contains an AVR ATMEGA32U4 8bit microcontroller, 16
MHz clocks (16 MIPS), 25 Input/output lines and a USB client port. It added a new fea-
ture to support USB Keyboard, Mouse, and MIDI and hence became very popular for
many enthusiast keyboard projects, either as a keyboard controller ( eg the Phantom,
tenkeyless keyboard ). By using teensy 2.0, Paul created a code of stream USB pack-
age for LEDs and this was used to build great LED projects. The Teensy++ 2.0 with an
AT90USB1286 chip that has 46 I/O lines.
Teensy 3.x: The Teensy 3.0, Teensy 3.1 and Teensy 3.2 have microcontrollers with
ARM Cortex M-series CPUs (48Mhz, 72MHz, 72MHz clock respectively). Compare to
other teensy’s, this version of teensy has faster processor and memory increased to four
times. Because of increased memory size, many audio applications are developed as an
Chapter 1. Introduction 15
audio library. The latest version is teensy 3.6 and details are explained in section 2.8.
Teensy LC: It has impressive capabilities and makes the project must simpler and it
is ideal for ’Internet of Things’ projects. It features an ARM Cortex-M0+ processor at 48
MHz, 62K Flash, 8K RAM, 12-bit analog input and output, hardware Serial, SPI & I2C,
USB, and a total of 27 I/O pins.
The Teensy audio library consists of a set of objects that enable recording, processing,
and playback of audio sampled at 44.1 kHz[10]. When the teensy is operating in I2S
slave mode, the audio library can be configured to work at sampling frequencies like
48 KHz, 96 KHz by changing the define AUDIO SAMPLE RATE EXACT. The Objects
instantiate particular audio functionalities, e.g., a waveform synthesizer, reverb, I2S
input, I2S output and finite-impulse-response (FIR) filters etc. By creating a new object,
a new functionality can be added. A cascade of objects forms a processing chain that
performs a set of operations on inputs to produce the desired output[10]. For example,
two or three I2S objects can be given to mixer, which produces the desired output ( as
shown in figure 1.1 ). The audio library defines each object in the chain to operate on
128 audio buffered samples.
The important function of Teensy audio library related to audio stream connects are
described in the table 1.3 [11]
Chapter 1. Introduction 16
Function Description
AudioConnection myConnec- Route audio data from the source object’s sour-
tion(source, sourcePort, destina- cePort, to the destination object’s destination-
tion, destinationPort) Port.
AudioConnection myConnec- Route audio data from the source object’s out-
tion(source, destination) put 0, to the destination object’s input 0.
AudioMemory(numberBlocks) Allocate the memory for all audio connections.
The numberBlocks input specifies how much
memory to reserve for audio data. Each block
holds 128 audio samples
AudioMemoryUsage() Returns the number of blocks currently in use.
AudioMemoryUsageMax() Return the maximum number of blocks that
have ever been used. This is by far the most
useful function for monitoring memory usage.
AudioMemoryUsageMaxReset() Reset the maximum reported by AudioMemo-
ryUsageMax. If you wish to discover the worst
case usage over a period of time, this may be
used at the beginning and then the maximum
can be read.
The AD1938 codec based CTAG face 2/4 sound card can receive 4 input channels
and transmit 8 output channels. The AD1938 codec registers can be configured using
the SPI interface. The CTAG audio data is transmitted and received to and from the
Teensy using I2S interface. The CTAG sound card has a crystal oscillator which is gen-
erating a clock at 24.576MHz. This generated clock is used to generate I2S clock signals
when configured as I2S master. When two CTAG face 2/4 sound cards are connected in
daisy chain mode the system can receive 8 input channels and transmit 16 output chan-
nels. For the test purpose the input signal from the PC is fed to any one of ADC input
of CTAG sound card and output from any one of DAC’s is connected to the speaker.
The PCM pass-through program controls the routing of which ADC input shall go to
which DAC output.
The second part of the thesis deals with implementation of a Freeverb algorithm
and testing this reverb effect on real-time audio data that is received from the audio
driver. Due to the memory limitation on the Teensy 3.6 board, we can test the reverb
effect on only two channels.
Chapter 1. Introduction 17
The Freeverb build blocks like comb filters and all-pass filter are implemented in
fixed-point arithmetic’s to be compatiable with older teensy boards, reduce the CPU
computational and increase the precision. The final mixing of the reverb output (wet)
and direct(dry) signal is implemented using the existing mixer function present in the
audio library. The reverberation effect is analysed by varying the feedback, damping
factors of combo filter and gain of all pass filter.
The performance, latency of audio driver and the memory consumption and CPU
performance of the freebverb is measured.
18
Chapter 2
Technical Background
As the transmitter and receiver have the same clock signal for data transmission,
the transmitter as the master has to generate the bit clock, word-select signal, and data.
In a complex system where there are more transmitters and receivers, it is difficult to
define the master. So system master controls the audio data flow between the ICs. In
such condition, the transmitter generates the data and act as a slave. Figure 2.1 illus-
trates some simple system configurations and the basic interface timing.
According to the I 2 S specification [12], the bus consists of basic three lines: contin-
uous serial clock(SCK), word select(WS), serial data(SD) (see Figure 2.1).
Name Function
SCK Its the output serial clock
• WS = 0; channel 1 (left);
• WS = 1; channel 2 (right).
over a signal data line is known as Multiplexer. It accepts the input (data streams) from
each source and divide each signal into units of the same size, and assign the units to
the composite data in a rotating, repeated sequence. At the receiving end, this compos-
ite signal is separated by a equipment called De-multiplexer. The prerequisite for the
successful TDM process is that the transmitter and the receiver have the same number
of audio channels to be transmitted, their sampling depth or the TDMS slot width and
the sampling rate.
Chapter 2. Technical Background 20
The TDM interface is similar to the 2-Channel Serial Audio Interface, with the ex-
ception that more channels, typically 4, 6 or 8, are transmitted within a sample frame
or sample period[13] as shown in Figure 2.2. As with the 2-Channel Serial Audio Inter-
face, the TDM interface is consists of two control clocks, a frame synchronization pulse
(FSYNC) and serial clock (SCLK), and the serial audio data line (SDATA).
Frame Synchronization Pulse - The FSYNC pulse is simply to identify the begin-
ning of a TDM frame (at the rising edge or falling edge of the pulse). This frame rate is
always at the audio sample rate, such as 44.1 kHz, 48 kHz, etc.
Channel Block - Each channel block consists of the audio data word succeeded by
a sufficient number of zero data bits to complete the N-bit channel block. For example
for a channel block of 32 bit with 24 bit audio data appended by 8 zero data bit.
Serial Clock - The main purpose of the serial clock is to shift the audio data into or
out of the serial audio ports[13, pg.2]. For example, 8 TDM slots each with a width of 32
bits and a sampling rate of 48 kHz, a minimum clock frequency fCLK = 8 · 32 · 48kHz
= 12, 288MHz required.
A standard SPI bus is a form of four wired communication interface, these wire
carry the information needed for the communication between SPI-master and SPI-
slave. The signal wires include Master Out Slave In (MOSI), Master In Slave Out
(MISO), the clock (SCK), and Slave Select (SS) (as shown in Figure 2.3). The SPI bus
has one master and one or more slaves, master activates the slave via the chip select
Chapter 2. Technical Background 21
The SPI bus operate in 4 different modes.These unique modes are defined by Clock
polarity (CPOL) and clock phase (CPHA) (specified as "0", "1") as shown in figure 2.4.
The detailed description of the available modes and data capture are summarized in
the table 2.2.
Chapter 2. Technical Background 22
• DMA transfers the data with maximum speed thus is useful for high-speed data
acquisition devices
• DMA also minimizes latency in the device as the dedicated hardware responds
more quickly than interrupts, and also transfer time is short. Minimizing latency
means, reducing the amount of temporary storage (memory) required on an I/O
device[17].
Chapter 2. Technical Background 23
• DMA also off-loads the processor, therefore, the processor is not used for handling
the data transfer activity and is available for other processing activity.
• DMA also increases overall system utilization as data transfer actually occurs in
parallel.
2.6 CTAG
CTAG face 2|4 is an I2S sound card based on AD1938 audio codec by Analog Devices
Inc[18]. It is 4 layer PCB which also include ground and power panel. Soundcard
contains 3 logical section, there are: 1. powder supply(digital 3.3V and 3.3v+5v analog),
2. Codec consists of reset circuitry and digital I/O and 3. Analog I/O.
The Following describes the summary of some characteristics and features from the
AD1938 data-sheet [19]:
• 94 dB THD + N
Functional Block Diagram (as shown in Figure 2.5 ) and Description of each block:
Chapter 2. Technical Background 24
In the AD1938 there are four ADC channels that are configured as two stereo pairs with
differential input. The ADCs operates at a different sampling rate of 48kHz, 96kHz
or 192kHz. It includes digital filters with 79db stopband attenuation. The Digital out-
puts are provided by the two serial data output pin, one for each stereo pair and a LR
clock(ALRCLK) and bit clock (ABCLK).
ADC Control registers: There are three ADC control registers. ADC control register
0 configures bits related to the power-down mode, High-pass filter and output sam-
ple rate . ADC control register 1 defines ADC serial formats, word width, serial delay
clock and bit clock (BCLK) active edge bits. similarly, programming ADC control reg-
ister 2, configures LRCLK polarity and format, BCLK source, polarity and format and
operating in either master or slave mode.
The AD1938 DAC channels configured as four stereo pairs(8 analog output). It includes
digital filters with 70db stopband attenuation. Digital inputs are supplied through four
serial data input pins (one for each stereo pair), a common frame clock (DLRCLK), and
a bit clock (DBCLK)[19].
DAC Control registers: There are five DAC control registers. DAC control register 0
defines power-down mode, Sample rate, serial data delay, serial format. DAC control
register 1 configures LRCLK Polarity , BCLK per frame, BCLK source, BCLK Polarity,
LRCLK and BCLK master/slave mode bits. DAC control register 2 programs master
mute, word width, output polarity. DAC Individual Channel Mutes register, as name
Chapter 2. Technical Background 25
implies mutes individual DAC channels. DAC Volume Controls register control the
output volume.
The onboard phase locked loop (PLL) can be selected to reference the input sample
rate either from the LRCLK pins or 256, 384, 512, or 768 times the sample rate from the
MCLKI/XI pin ( 48 kHz mode )[19]. In an AD193x family, if a device is programmed
at 256x f s , the frequency of master clock input is 256 x 48 KHz= 12.288 MHz. This fre-
quency of the master clock should remain same even the device operates at 96 kHz or
192KHz (which become 128x f s and 64x f s respectively).
By default, onboard PLL generates the internal master clock from an external clock.
This internal master clock for ADC is 256 x f s for all clock modes but for DAC it
varies according to the modes: 512x f s ( 48KHz mode), 256x f s ( 96KHz mode) and 128x f s (
192KHz mode). By setting the correct values in PLL and control register 1, a direct mas-
ter clock 512 x f s can be used either for ADC or DAC.
PLL and Clock control registers: There are two PLL and Clock control registers. PLL
and Clock control register 0 defines PLL power-down mode, master clock rate setting,
PLL input and internal master clock enable. PLL and clock control register 1 configures
ADC and DAC clock source selection either from PLL or master clock.
The Figure 2.7 describes the clock generation logic of the teensy’s processor. The clock
generation logic divides the clock sources for the various domains like system clock bus
clock, flash memory and for peripheral module-specific clock.
The following table 2.4 describes the important clocks needed for the I2S clock gen-
eration from the previous block diagram
The I2S audio master clock (MCLK) can be generated either from system clock
(SYSCLK) or system oscillator clock (OSCERCLK) or peripheral clock( MCGPLLCLK,
MCGFLLCLK, IRC48MCLK, USB1PFDCLK ). MCLK is used to generate the bit clock
and the frame sync of I2S/SAI. When using I2S as slave, the bit clock and frame sync
are generated externally. Figure 2.8 illustrates I2S clock generation of the teensy3.6.
The Teensy software can be visualized into four layers (as shown in Figure 2.9.
1. Drivers
This layer contains the basic driver classes that are needed to communicate to
external devices like SPI, USB, Serial Terminal, GPIO pin.
Chapter 2. Technical Background 29
3. Audio Library
This library contains the utilities for audio analysis like, fft, peek detection, filter,
mixer and audio data capture, audio data playback from the SD card. There are
utilities to generate various waveforms like sine, sweep and ramp signal.
4. Audio Application
The audio application can be developed using the audio library and drivers.
Teensy audio library development group has set of rules for creating new objects and
their naming conventions. Audio objects should follow consistent naming conventions
and adhere to common rules to make audio library usable by users of all skill levels. Ev-
ery object in the audio library begins with “Audio” followed by category name, which
is followed by a name that describes the function. Each name begins with capital letter.
The category names that are defined by teensy audio library are followed [22]
For example from the object name“AudioInputI2S” one can deduce that it is a object
which receives input data in I2S format.
Chapter 2. Technical Background 30
By using Teensy audio library, creating the own audio processing objects very easy [23].
C++ Object definition All the new objects should be derived from the “AudioStream”
base class. This base class provides all memory management (allocate, release), com-
munication with other audio objects(receive, transmit, connect) and CPU/memory track-
ing functions. The basic C++ Template using AudioStream is shown below
c l a s s AudioEffectTemplate : p u b l i c AudioStream
{
public :
AudioEffectTemplate ( ) : AudioStream ( 1 , inputQueueArray ) ;
v i r t u a l void update ( void ) ;
private :
a u d i o _ b l o c k _ t ∗ inputQueueArray [ 1 ] ;
};
• The eDMA engine ( defines the calculation of source and destination address ,
and data movement operations)
The TCD contains all the information about the data movement. This includes the
source and destination addresses, the address increment after each transfer and the
transfer size [24]. It operates in minor and major nested transfer loops
Each channel requires a 32-byte transfer control descriptor for defining the desired data
movement operation. The channel descriptors are stored in the local memory in se-
quential order: channel 0, channel 1, ... channel 31. Each TCDn definition is presented
as 11 registers of 16 or 32 bits. Prior to activating a channel, you must initialize its
TCD with the appropriate transfer profile. The table 2.5 summarizes the TCD Register
description
Chapter 2. Technical Background 32
Each DMA request will move the number of bytes configured in the NBYTES (minor
byte count) register. This corresponds to a minor loop [24]. When each minor loop is
transferred CITER(major loop count) register is decremented. When major loop count
reaches its half value DMA Interrupt is generated and data is copied. Similarly when
major loop count reaches zero(CITER register is equal to zero), once again DMA inter-
rupt is generated and data is transferred in to buffer.
Finally, one receiver is connected to the Nth device, which collects all the data from
the Nth device. Figure 2.12 shows how three (N = 3) devices work in a daisy chain. A
daisy-chained network can take two basic forms: linear and ring[26]. An advantage of
the ring topology over linear is that the number of transmitters and receivers can be cut
in half since there is a loop connection back from the last device to the first. The main
advantage of the daisy chain is its simplicity. Another advantage is scalability[27].
There are two types of filters in the digital realm: Finite Impulse Response (FIR)
filters and Infinite Impulse Response (IIR) filters.
Finite Impulse Response (FIR) Filter:- FIR Filter is a filter whose impulse response
is of finite duration as it settles to zero at finite time [28]. In other terms: if the input
signal has fallen to zero, the filter output will also fall to zero after a given number of
sampling periods. The Figure 2.13 shows an FIR filter structure of order N + 1. For
an FIR filter of order N, output is computed as a weighted sum all values of the filter
input, i.e.
where:
• y(n) is the output signal (given by linear combination of the last input samples
x(n-i)
• b(i) is the coefficient of the filter( i.e. give the weight for the combination)
FIR Filters have few useful properties that make it sometimes important. The ad-
vantage of use FIR filter over IIR filter are:
• very stable, the output is the summation of finite number of finite multiple of
input.
• With symmetric coefficient FIR filter providers linear phase response, sometime
this is very important in some applications like data communications, cross filter-
ing etc
• Also used in designing all-pass filter to suppress the phase response of standard
IIR filter.
The disadvantages of FIR filter are high CPU power consumption, low efficiency.
By designing the specialized hardware for FIR filters, one can make it as efficient as IIR
for many applications.
Infinite Impulse Response (IIR) Filter:- In DSP, IIR is one of the primary type of
digital filter. As name implies the "impulse response" is infinite, because of the feed-
back in the filter. If the impulse signal given to filter, an infinite number of non zero
values will be obtained, i.e it continues infinitely.
The figure 2.14 show the IIR filter example and the difference equation [29] which
explains how the output signal is compared to input (equation 2.3)
1
y(n) = (b0 x (n) + b1 x (n − 1) + .... + b p x (n − D )
a0
Where
The advantage of IIR filter over FIR is, the given filter characteristic can be achieved
with less memory and calculation. It also as many disadvantages like,
• If there is any error in output computation due to fixed point operations, this error
is propagated back through the feedback and it continuously effect the output.
There are many types of filters under this category like high pass filter, low pass, band
pass, stop band, notch, comb filter and all-pass filter. For this thesis comb and all-pass
filters are required, so description of these filters will be explained below.
Comb Filter:
A comb filter is generated by mixing the delayed version of the input signal to itself,
causing constructive and destructive interface. A comb filter can be generated either
from FIR or IIR filters. The frequency response of a comb filter appears as series of reg-
ularly spaced notches, giving the appearance of a comb. Thus the name comb is coined.
There are two different types of Comb filters depending on the direction in which de-
layed signal added to input like Feedforward and Feedback.
• It can be used to model the effect of acoustic standing waves in a cylindrical cavity
multiplierless filters
Chapter 2. Technical Background 37
Feedforward form:- Feedforward comb filter is one of the simplest FIR filter [30] and
the general structure is shown in the Figure 2.15. The frequency response of this filter is
periodic, its drops periodically to minimum value and rises periodically to maximum
value.
It can be described by the difference equation, where K is the delay length, and α is
a scaling factor applied to the delayed signal.
Y (z)
H (z) = (2.5)
X (z)
H (z) = 1 + αz−K (2.6)
zK +α
H (z) = (2.7)
zK
Feedback form:- The feedback comb filter is a simple type of infinite impulse re-
sponse (IIR) filter [30] and the general structure is shown in the Figure 2.16
Y (z)
H (z) = (2.9)
X (z)
zK
H (z) = K (2.10)
z −α
All-pass filter
The All-pass filter is one of the important blocks while building the digital audio sig-
nal processing system. It is a filter where the amplitude is unity for all frequency but
introduces the phase shift [31]. It behaves as phase equalizer. The main purpose of
using an all-pass filter is to add a phase shift to the response of the system. The all-pass
filters are commonly used in an audio application like filter banks, speaker crossover,
and reverberators. For artificial reverberators, the basic structure of the all-pass filter is
shown in Figure 2.17. It is constructed by using delay with feedforward and feedback
paths (This structure, known as the Schroeder all-pass comb filter).
Floating-point representation
Floating point represent a number in a wide range from very small (2−17 ) to very large
(270 ). IEEE 754 defines the floating point numbers with three fields: S sine bit, e an
exponent, f mantissa(fractional) [32].
Chapter 2. Technical Background 39
• double precision: it contains total 64bits, which includes one sign bit (positive or
negative) , 11 bits exponent and 52 bits fractional.
Advantages:
Disadvantage:
The floating-point numbers are multiplied by a scaling factor and make floating
numbers as integers. The scaling factor depends upon how many fractional positions
are absorbed in the integer. The scaling factor can be a power of 10(decimal) or power
of 2 (binary). When the scaling factor is a power of 2 then it is called binary scaling. The
number of bits of scaling factor determines the virtual binary point.
After the arithmetic operations, the integer is divided by the same scale factor (re-
scaling) to remove the scaling. In binary scaling division can be achieved by right shift-
ing the number.
Where m is a number of bits for the signed integer part, n is a number of bits for the
fractional part( scaling factor 2n ), m+n is the total word length, it can be 16 bits or 32
bits. For example, Q1.15 means 1 bit signed integer part and 15 bits for the fractional
part.
Pros
• The fixed point has more precision than float point
2.13 Reverberation
Natural reverberation is the combined effect of multiple sound reflections within a
room[33]. Once the source of the sound stops, the reverberation in a room causes the
perceived sound to decay at a smooth and gradual rate. In the real world, reverberation
characteristics are influenced by the dimensions of the actual room size, the construc-
tion of the room (wall materials), objects found in the room( i.e furniture) and diffusion.
Normally there is always a direct path from source to the listener but the sound
waves travel by bouncing at the walls and surfaces in the room to the listener. These
waves take longer time and path, with less energy. In the figure 2.18, as sound waves
may be reflected several times before reaching the listener, the reflections attenuate over
time [34].
The figure 2.19 shows the impulse response plots for two very different spaces; a
large concert hall and a cathedral [35]. As the sound waves travel, it reflects the nearby
structures thus causing the first echoes. These initial echoes are called as Early Reflec-
tions. Since reflections are direct reflection it has high energy and difficult to implement
but important for the simulation of reverb. The sound waves continue to expand and
generate more reflections, with the reflected signals adding up with other reflected sig-
nals while decaying in energy. The resulting reverb “tail” is called late reverberation
or subsequent reverberation [35]. The late reflections have a denser characteristic with
lower energy and produce a sound with more persistence. The persistence is more be-
cause one can hear the small version of an original sound source for long after that
source has fadeout. The time taken for sound energy to get below audible thresholds
for human hearing is termed reverb or reverberation times [36].
Chapter 2. Technical Background 41
F IGURE 2.19: The impulse responses of a large hall and cathedral. [35,
Figure 11.1]
RT60 reverb time is one of the most well-known measurements for reverb. The amount
of time required for reflections of a direct sound to decay 60 dB is reverb time or RT60 .
Sabine worked on reverberation and established a relationship between the RT60 of a
room, its volume, and its total absorption (in sabins) [37]. and derived the following
formula in Equation 2.13 :
VR
RT60 = 0.5 (2.13)
SR A RAve
Where
VR = volume of room in f t3
SR = surface area of room in f t2
A RAve = average absorption coefficient
In addition to natural reverb, one can also reproduce the reverberation effect using
software (developed using audio cards, synthesizers, processors, and digital audio ap-
plications). In 1920, the need for the artificial reverberation first raised especially for
the music studio and recordings [38].
Chapter 2. Technical Background 42
Schroeder Reverberation
In 1960, Manfred Schroeder developed the first digital reverberation algorithm by us-
ing four parallel comb filter with two delay-based all-pass filters in series as shown in
figure 2.20. In this structure, the comb filters produce the long echoes that occur be-
tween the walls in a hall or room. but these parallel comb filters do not build up the
echoes as in the realistic situation. To increase the echo density the parallel comb filter
output is fed to all-pass filters connected in series. These all-pass filter multiplies the
echoes to generate the reverberation effect and avoid coloration as all-pass filters have
the flat frequency response.
Schoerder suggested few properties for selecting comb filters and all-pass filter as
follows: [35]
• choose the delay lengths that are typically mutually prime and spanning succes-
sive orders of magnitude, e.g., 1051, 337, 113 .
−3DTs
g = 10 RT60
(2.14)
where
Ts = sample period
Chapter 2. Technical Background 43
• Select the delay that are smaller than comb filters (1ms to 5ms)
Moorer’s Reverberation
In the real room, the high frequencies decay much faster than low frequency, this cannot
be achieved using Schroeder reverberator. Other problems with Schroeder suggested
structure is that the echo density doesn’t adds-up sufficiently as required and for im-
pulsive sounds, the response is fluttery and metallic. To overcome the above problems
James Moorer suggested improvements (as shown in figure 2.21) to Schroeder reverber-
ator structure by adding two additional comb filters with Low pass filter in the feedback
path and only one all-pass on the output. These additional comb filters increase the
echo density and with the addition of low pass filter generates the frequency depen-
dent air absorption [39]. However, the Moorer’s reverberator produces much better
reverberation effect as compare to Schroeder but still sensitive to impulsive sounds.
The figure 2.21 is the block diagram of low pass comb filter. By placing low pass filter
in the feedback loop not only remove the high frequencies but also generate the short
impulsive sounds by smearing the echoes [40].
Chapter 2. Technical Background 44
The difference equation of comb feed farword filter (equation 2.15) and Low pass
filter(LPF) (equation 2.17) is as follows
z− D
HC (z) = (2.15)
1 − g1 z − D
1
HLP (z) = (2.16)
1 − g2 z − 1
According to th figure 2.22 the Lowpass comb filter equation can obtain by multi-
plying the LPF transfer equation to feedback g1 Z −z
z− D
H (z) = (2.17)
1 − HLP (z) g1 z− D
y ( n ) = x ( n − D ) − g2 x ( n − D − 1 ) + g2 y ( n − 1 ) + g1 y ( n − D ) (2.18)
The basic block diagram of each reverberator channel consists of 8 parallel Schroeder-
Moorer lowpass comb-filters and four series Schroeder all-pass filter. The figure 2.24
shows the block diagram of the freeverb left audio channel with filter delay lengths. As
per the suggestion by Jezar, the freeverb right stereo channel can be obtained by adding
an integer value 23 to delay lines of all 12 filters. This integer is known as stereospread
[41].
In the Mixer block the directed signals and the reflected signals are mixed to provide
reverberation effect.
" # " # " # " #
outputL inputL wet1 wet2 outputL
= dry +
outputR inputR wet2 wet1 outputR
where
• dry = this parameter defines how much original signal should be mixed to output.
• wet1 and wet2 = this parameter defines how much reverberation signal is mixed
with output.
Chapter 2. Technical Background 46
The lowpass comb filter in freeverb is similar to lowpass comb filter suggested by
Moorer. But lowpass used is a unity gain one-pole as shown in figure 2.21. The lowpass
filter gain was selected as g1 = 1-d so that filter has unity gain at zero frequency and at
high frequencies is a relatively small gain. The transfer function of the low pass filter is
given below
1−d
H (z) = (2.19)
1 − dz−1
Therefore by substituting the lowpass filter equation 2.19 in the comb filter transfer
function equation 2.15 , the approximate lowpass comb filter transfer function is then
f ,d z− M
LBCFM = (2.20)
1 − f 1−1− d
dz−1
z− M
where
plugin parameter:
This section explains each user controllable effect parameters and their purposes [43]
Wet Mix:- Wet signals are the type of signals coming from reverberation and that
undergo modification and process. Dry signals are the raw /direct signals coming from
the source. The mixer controls the signal level by mixing the original dry signal with
the altered wet signal. It determines how much stereo separation occur in the reverber-
ation.
Roomsize (f ):- In freeverb algorithm roomsize is related to the feedback gain of low
pass comb filter. By increasing the roomsize (f increases) the reverberation time in-
creases. For more stability the f must be less than one.
Damping (d):- It gives the decay factor of the reverberation. Higher the damping
value, less reverb and decays fast. For stability the d must be less than one.
This thesis developed the control class for the AD1938 codec in Teensy Audio li-
brary with SPI control. It also extended the TDM I2S input and output class for the
both 8 and 16 channels and Teensy as I2S slave.
This thesis adds the reverb effect to the teensy audio library by using the free-
verb algorithm, developed by Jezar. During the development process, many digital
reverberation algorithms are studied. The first artificial reverberation was designed by
Schroeder using four parallel comb filters and followed by two series all-pass filters
[44]. The Schroeder reverberator does not have sufficient echo density and had very
poor response (Metallic) for the impulsive sounds [39].
Chapter 2. Technical Background 48
Moorer improved the Schroeder algorithm by adding more comb filters to increase
the echo density [40].A low pass filter is added to the feedback loop of the comb filters
to simulate the frequency dependent air absorption [39]. But still has poor response to
impulsive sounds.
For exponential buildup of the echoes, Gardner suggested using nested all-pass fil-
ters, where an all-pass filter is embedded to the delay line in another all-pass filter. The
echoes generated by inner all-pass filter are recirculated by the outer feedback path.As
a result, the echo density increases with the time [45] and reduced the metallic sound
of the all-pass filter. The nested all-pass filter design is more complex to implement.
There much more reverb algorithm, this thesis implements the freeverb algorithm.
The freeverb algorithm is based on Schroeder and Moorer reverberator, which is pro-
grammed in C++ by Jezar at Dreampoint is widely used in the open source commu-
nity. The teensy 3.6 has only 256KB RAM, a Freeverb algorithm for two-channel audio
stream can be implemented within this available memory.
49
Chapter 3
This chapter explains about hardware and software tool setup and details of implemen-
tation of individual components.
The Arduino IDE doesn’t come with built-in support for Teensy devices, so Paul Stoffre-
gen (the genius behind Teensy) created a simple application called Teensyduino which
allows you to program your Teensy directly from the Arduino IDE, as well as adjust
the clock speed, and USB device functions (serial, HID, MIDI etc. . . )[47]. Below are few
steps that have to follow for the installation of teensyduino on windows PC.
Once the installation is finished, restart the application. Now navigate to Tools>Board:
the list of Teensy Boards are displayed and select the teensy 3.6. Set Clock speed to
196MHz Optimize speed and ensure USB device type selected as ’serial’.
3.1.2 Setup
The two CTAG board connected to the teensy 3.6 forming daisy chain mode. The Figure
3.1 explains the hardware setup and how the connections are made physically. The
Chapter 3. Implementation and Hardware Setup 50
table 3.1 and 3.2 explains the Teensy 3.6 connects to the pins on the Master AD1938
sound card and Slave AD1938 sound card respectively.
TABLE 3.1: Pin connections of Master and Slave AD1938 AudioCard and
Teensy 3.6
Chapter 3. Implementation and Hardware Setup 51
TABLE 3.2: Daisy chain Pin connections between Master and Slave
AD1938 codec
Teensy 3.6
AD1938
The summary of characteristic and features of Codec AD1938 is available in the Datasheet[19]
AD1938 codec support the daisy chain mode configuration for both ADC and DAC
data transfer.
Two AD1938 are connected in daisy chain mode to expand the system to 8 ADCs
and 16 DACs. The second AD1938 in the above diagram which acts as I2S master
provides the bit clock(BLCK) and frame sync (LRCLK)for both Teensy and first AD1938
codec. The second AD9138 codec registers are configured to use its ADC clock as an
I2S master for the DAC unit (as shown in figure 3.2).
Chapter 3. Implementation and Hardware Setup 52
The first AD1938 codec ADC output data (4 ADC) is pushed into the second AD1938
codec. The second AD1938 combines this received ADC data with its own ADC data
(4 ADC) and outputs combined ADC data (8 ADC) to Teensy TDM_IN (explained in
Figure 3.3).
When Teensy sends 16 channels through serial port (TDM out) to second AD1938, the
second AD1938 separates the first AD1938 DAC data and its own DAC data. The first
AD1938 data is pushed through DSDATA2 to the first AD1938 (see Figure 3.4).
F IGURE 3.4: Single-Line DAC TDM Daisy-Chain Mode [19, Figure 18]
Chapter 3. Implementation and Hardware Setup 53
These following are various DAC daisy chain configurations which are possible.
The AD1938 audio control class is derived from the base class of "AudioControl". This
base AudioControl class contains functions for all the codecs and some functions may
not be needed for AD193x. In AD1938 there is no gain control for ADC and no sup-
port input source selection, for this reason, inputLevel() and inputSelect() function not
needed. The following section gives the details how to implement AD1938 control class.
The AD1938 has an SPI control port that allows writing and reading internal control
registers of ADCs, DACs, and clock system. The high-level SPI port write and SPI port
read function for AD1938 are developed using the existing SPI library and wire library
functions.
Configure the Teensy pins, clatch and reset out pins as output. To reset the Ad1938 by
pulling the reset out pin to low and hold for some time and then pull it back to high
using the pinMode function of wiring library.
pinMode ( a d 1 9 3 8 _ c l a t c h , OUTPUT) ;
pinMode ( a d 1 9 3 8 _ r e s e t , OUTPUT) ;
d i g i t a l W r i t e ( a d 1 9 3 8 _ r e s e t , LOW) ;
delay ( 2 0 0 ) ;
d i g i t a l W r i t e ( a d 1 9 3 8 _ r e s e t , HIGH) ;
delay ( 4 0 0 ) ; //wait f o r 300ms t o load t h e code
After the reset, most of the registers in the Ad1938 codec will initialize with default
values. The initialization will run and PLL acquires the lock state.
The teensy SPI library uses some pins as default SPI pins(10,11,12,13) as shown in the
pin diagram figure 2.6. If the default pins are used for another purpose, one can recon-
figure alternate pins as SPI pins using the SPI library functions. The function begin()
will initialize the Teensy SPI port registers with configured clock, MISO (master in slave
out), MOSI (master out slave in) pin information.
/∗ SPI c l o c k pin s e t ∗/
SPI . setMOSI ( c i n ) ;
SPI . setMISO ( cout ) ;
SPI . setSCK ( c c l k ) ; /∗ SPI c l o c k a l t e r n a t e pin ∗/
SPI . begin ( ) ;
The SPI clock frequency, transmission byte order, and the SPI clock mode are config-
ured using the function SPISetting from the library. .
The AD1938 can operate up to maximum 10 MHz SPI clock frequency. In this
project, 1 MHz clock is used. SPI library has two additional bus protection functions to
avoid corrupting the ongoing SPI operations.
/∗ e n a b l e s t h e SPI i n t e r r u p t s ∗/
SPI . e n d T r a n s a c t i o n ( ) ;
The SPI slave device is selected by pulling the GPIO pin to low. During the start of
the SPI communication, this pin is pulled to LOW and kept low until all the bytes are
transmitted. At the end of the SPI transfer once again this pin is pulled to HIGH.
d i g i t a l W r i t e ( a d 1 9 3 8 _ c l a t c h , LOW) ; /∗ s l a v e s e l e c t ∗/
d i g i t a l W r i t e ( a d 1 9 3 8 _ c l a t c h , HIGH) ;
AD1938 codec uses the following SPI protocol (as shown in figure 3.5 ) to exchange
register value with SPI master. The global address of AD1938 is 0x4 as per the data
sheet. This global address is shifted by one bit and appended with read (1)/write (0)
bit. The MSB byte is 0x9 while reading the data and 0x8 while writing the data.
The AD1938 codec register can be configured by sending the 3 bytes using SPI library
transfer function.
To read a register from AD1938 , first send the address of the register along with the
read command and then AD1938 will answer the register value. This reading one byte
can be achieved by passing 0 to SPI transfer function.
Combing SPI setting, slave select we can develop spi port read function and write func-
tions.
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
spi_read_reg
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
unsigned char s p i _ r e a d _ r e g ( unsigned char reg )
{
unsigned char r e s u l t = 0 ;
unsigned char data [ AD1938_SPI_WRITE_BYTE_COUNT ] ;
data [ 0 ] = AD1938_READ_ADDRESS ;
data [ 1 ] = reg ;
data [ 2 ] = 0 x0 ;
// and c o n f i g u r e s e t t i n g s
SPI . b e g i n T r a n s a c t i o n ( S P I S e t t i n g s ( AD1938_SPI_CLK_FREQ ,
MSBFIRST ,
SPI_MODE3 ) ) ;
// t a k e t h e chip s e l e c t low t o s e l e c t t h e d e v i c e :
d i g i t a l W r i t e ( a d 1 9 3 8 _ c l a t c h , LOW) ;
return ( r e s u l t ) ;
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
/∗ spi_write_reg
∗/
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
bool s p i _ w r i t e _ r e g ( unsigned char reg , unsigned char v a l )
{
unsigned char data [ AD1938_SPI_WRITE_BYTE_COUNT ] ;
// and c o n f i g u r e s e t t i n g s
SPI . b e g i n T r a n s a c t i o n ( S P I S e t t i n g s ( AD1938_SPI_CLK_FREQ , MSBFIRST ,
SPI_MODE3 ) ) ; // t a k e t h e chip s e l e c t low t o s e l e c t t h e d e v i c e :
d i g i t a l W r i t e ( a d 1 9 3 8 _ c l a t c h , LOW) ;
return true ;
The AD1938 codec contains 17 registers to configure PLL, ADC, DAC and also for mut-
ing and volume increase. Each register is 8 bit length and valid values have to be set
according the Ad1938 data sheet.
1. Config
2. Enable
3. Disable
4. Volume
Chapter 3. Implementation and Hardware Setup 58
5. DAC mute
6. ADC mute
1. Config
This function is generalized and take the input parameters like sample rate, number of
channels and number of bits per channel similar to ALSA driver. There is one addi-
tional parameter to configure each AD1938 codec either I2S master or slave.
CTAG face 2 4 board contains crystal oscillator operating at 512fs (24.576 MHz).
This will be used to generate clock in master mode. In slave mode the I2S clock is gen-
erated from the ADC LR clock. This function uses the received input parameter to set
the AD1938 codec register values.
2. Enable
This function enables the PLL mode to normal mode and set the ADC and DAC units
active by setting the PLL Control register
AD1938 Master:
AD1938 Slave:
3. Disable
To reduce the power consumption system this functions pull PLL, ADC and DAC units
into power down mode and disables the internal clocks of ADC and DAC by writing
to the registers.
4. Volume
The codec has a precision of 3/8 db for each step. The input parameter is converted
accordingly and value is get for all the 8 DAC registers .
5. DAC mute
Instead of reducing volume step by step , one can mute the all DAC by configuring
registers.
s p i _ w r i t e _ r e g (AD1938_DAC_CHNL_MUTE, 0 x f f ) ; /∗mute∗/
s p i _ w r i t e _ r e g (AD1938_DAC_CHNL_MUTE, 0 0 ) ; /∗unmute∗/
6. ADC mute
This class contains all the necessary private variable and hence it can be used in multi-
ple instances.
c l a s s AudioControlAD1938 : p u b l i c AudioControl
{
public :
bool s p i I n i t ( i n t c l a t c h , i n t r e s e t , i n t cout , i n t cin , i n t c c l k ) ;
bool c o n f i g ( Te_samplingRate sampleRate ,
Te_bitsPerSample wordLen ,
Te_i2sNumChannels numChannels ,
Te_i2sClockMode mode ) ;
bool e n a b l e ( void ) ;
bool d i s a b l e ( void ) ;
bool volume ( f l o a t volume ) ;
bool adcMute ( bool mute ) ;
bool dacMute ( bool mute ) ;
void r e a d A l l r e g ( void ) ;
bool i n p u t S e l e c t ( i n t n ) {
r e t u r n ( n == 0 ) ? t r u e : f a l s e ;
}
bool i n p u t L e v e l ( f l o a t volume ) {
return f a l s e ;
}
Chapter 3. Implementation and Hardware Setup 62
};
void setup ( ) {
/∗ r e s e t codec and i n i t s p i pi ns ∗/
ad1938master . s p i I n i t ( 7 , 1 7 , 1 2 , 1 1 , 1 4 ) ;
/∗ c o n f i g u r e codec ∗/
ad1938master . c o n f i g ( FS_48000 , BITS_16 , I2S_TDM_16CH , AD1938_I2S_MASTER ) ;
/∗ a d j u s t t h e volume∗/
ad1938master . volume ( 1 ) ;
/∗power up t h e adc ∗/
ad1938master . e n a b l e ( ) ;
}
void loop ( ) {
// put your main code here , t o run r e p e a t e d l y :
For the current project of daisy chain, we need 8 channel input and 16 channel output
and Teensy as I2S slave. To meet the requirement criteria the AduioOuputTDMSlave
and AudioInputTDMSlave classes are developed and they are derived from AduioOuputTDM
and AduioInputTDM classes respectively.
The teensy registers are configured for receiving data in slave mode. The function con-
fig_tdm configures the kinetic register.
1. Clock configuration
Chapter 3. Implementation and Hardware Setup 63
• Enable clock for I2S module, DMA mux and DMA by setting the registers
below
SIM_SCGC6 |= SIM_SCGC6_I2S ;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
SIM_SCGC7 |= SIM_SCGC7_DMA ;
• Configure Master clock as slave, selects the input clock zero to the MCLK
divider and set the division register to zero
I2S0_MCR = I2S_MCR_MICS ( 0 ) ;
I2S0_MDR = 0 ;
2. Transmitter configuration :
I2S0_TMR = 0 ; // Enable a l l t h e b i t s i n a word
I2S0_TCR1 = I2S_TCR1_TFW ( 8 ) // s e t t h e water mark
I2S0_TCR2 = I2S_TCR2_SYNC ( 0 ) | I2S_TCR2_BCP ; //Configured f o r
asynchronous mode o f o p e r a t i o n and B i t c l o c k i s a c t i v e low with
d r i v e outputs on f a l l i n g edge and sample i n p u t s on r i s i n g edge .
I2S0_TCR3 = I2S_TCR3_TCE ; // A channel i s enabled b e f o r e FIFO
operations
Chapter 3. Implementation and Hardware Setup 64
3. Receiver Configuration
I2S0_RMR = 0 ; // c o n f i g u r e r e c e i v e word i s e n a b l e
I2S0_RCR1 = I2S_RCR1_RFW ( 8 ) ; // s e t t h e water mark
I2S0_RCR2 = I2S_RCR2_SYNC ( 1 ) | I2S_TCR2_BCP ; //Configured f o r
Synchronous with t r a n s m i t t e r and Clock P o l a r i t y i s s i m i l a r t o
transmitter register
I2S0_RCR3 = I2S_RCR3_RCE ; // Receive data channel N i s enabled .
I2S0_RCR4 = I2S_RCR4_FRSZ ( 1 5 ) | I2S_RCR4_SYWD ( 0 ) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP| I2S_RCR4_FSD ; //
Configure number o f c h a n n e l s/ words i n a frame ,
Sync Width , MSB i s t r a n s m i t t e d f i r s t , Frame Sync
Early , Frame sync i s a c t i v e low
I2S0_RCR5 = I2S_RCR5_WNW ( 3 1 ) | I2S_RCR5_W0W ( 3 1 ) | I2S_RCR5_FBT ( 3 1 ) ; //
Configure each Word width ( e x c e p t f i r s t word o f t h e frame ) , f i r s t
word width and MSB F i r s t B i t S h i f t e d
The following function initializes the transmitter DMA configuration and config-
ures the TDM registers
AudioOutputTDMslave : : config_tdm ( ) ;
dma . TCD−>SOFF = 4 ;
// D e f i n e s t h e number o f b y t e s t o t r a n s f e r per r e q u e s t
dma . TCD−>NBYTES_MLNO = 4 ;
// p o i n t s t h e d e s t i n a t i o n data i . e I 2 S t r a n s m i t r e g i s t e r
dma . TCD−>DADDR = &I2S0_TDR0 ;
//Connects t h e s o u r c e DMA t o I 2 S t r a n s m i t
dma . triggerAtHardwareEvent (DMAMUX_SOURCE_I2S0_TX) ;
u p d a t e _ r e s p o n s i b i l i t y = update_setup ( ) ;
// r e s e t t h e t r a n s f e r c o n t r o l r e g i s t e r
I2S0_TCSR = I2S_TCSR_SR ;
//Map t h e I n t e r u p t s e r v i c e r o u t i n e t o DMA
dma . a t t a c h I n t e r r u p t ( i s r ) ;
Chapter 3. Implementation and Hardware Setup 66
The following function initializes the receiver DMA configuration and configures the
TDM registers
AudioOutputTDMslave : : config_tdm ( ) ;
//Memory address p o i n t i n g t o t h e I 2 S R e c i e v e r r e g i s t e r
dma . TCD−>SADDR = &I2S0_RDR0 ;
// f o u r b y t e s a r e r e c i e v e d per r e q u e s t
dma . TCD−>NBYTES_MLNO = 4 ;
// p o i n t s t h e d e s t i n a t i o n data i . e r c e i v e r b u f f e r
dma . TCD−>DADDR = tdm_rx_buffer ;
//Connects t h e s o u r c e DMA t o I 2 S r e c e i v e r
dma . triggerAtHardwareEvent (DMAMUX_SOURCE_I2S0_RX) ;
u p d a t e _ r e s p o n s i b i l i t y = update_setup ( ) ;
//Map t h e I n t e r u p t s e r v i c e r o u t i n e t o
DMAdma. a t t a c h I n t e r r u p t ( i s r ) ;
The AudioOputTDM class transmit the data from tx_buffer to I2S_TDR0 through
the eDMA. When tx_buffer is half emptied the DMA generates interrupt. In the inter-
rupt service routine (ISR) the data is combined from different channels and copied to
tx_buffer. When there is no data available then zeros are copied to the tx_buffer.
The PCM pass through application is explained in the form of a flow chart below.(see
Figure 3.8)
Chapter 3. Implementation and Hardware Setup 68
This implementation uses the input signal from I2S data coming from audio driver
AudioInputTDMslave class. This signal is fed to mixture AudioMixer4 class (mixer1)
which combines left and right channel with gain multiplication. The output of the
mixer1 is given to two freeverb objects ( AudioEffectFreeverb class), one for left chan-
nel reverb and other for the right channel reverb. The Left output can be derived from
mixing the direct input signal (left dry), reverberated left signal (wet1) and reverber-
ated right signal (wet2). Similarly the right output can be derived from mixing the
direct input signal (right dry), reverberated right signal (wet1) and reverberated left
signal (wet2) with appropriate gains received from the user plugin. The left output
from mixer2 and right output from mixer3 are given to output of audio driver Au-
dioOutputTDMslave class to obtain the stereo output signal.
The freeverb objects is drived form the audio stream base class as shown in figure
3.10.
Chapter 3. Implementation and Hardware Setup 69
The AudioEffectFreeverb class contains initialization and the processing for all-pass
filter, lowpass comb filter. The flow of the freeverb is presented in the update function.
All the filter implementations are done using fixed point arithmetics. The functions are
explained below
This function implements the difference equations 2.21 of lowpass comb filter.
Chapter 3. Implementation and Hardware Setup 70
/∗
output = w( n−M) ;
z1 = ( output ∗ (1 − d ) ) + ( z1 ∗ d ) ;
w( n−M) = input + ( z1 ∗ f ) ;
∗/
f o r ( n = 0 ; n < AUDIO_BLOCK_SAMPLES ; n++)
{
bufout = l b c f −>p b u f f e r [ l b c f −>b u f f e r I n d e x ] ;
input = i n _ b u f [ n ] ;
/∗
bufout = w( n−M) ;
w( n−M) = input + bufout ∗ g ;
output = −w( n−M) ∗ g + bufout ;
∗/
for (n = 0 ; n < AUDIO_BLOCK_SAMPLES ; n++)
{
bufout = apf −>p b u f f e r [ apf −>b u f f e r I n d e x ] ;
input = i n _ b u f [ n ] ;
z1 = m u l t i p l y _ 3 2 x 3 2 _ r s h i f t 3 2 _ r o u n d e d ( bufout , apf −>gain ) ;
input += ( z1 << 2 ) ;
w= m u l t i p l y _ 3 2 x 3 2 _ r s h i f t 3 2 _ r o u n d e d (− input , apf −>gain ) ;
output =bufout +(w << 2 ) ;
out_buf [ n ] = output ;
apf −>p b u f f e r [ apf −>b u f f e r I n d e x ] = input ;
apf −>b u f f e r I n d e x ++;
i f ( apf −>b u f f e r I n d e x >= apf −>delay )
{
apf −>b u f f e r I n d e x = 0 ;
}
}
update
The update function combines eight parallel lowpass comb filter and four cascaded
all-pass filter. Each filter is operated on 128 samples as per the requirement of teensy
library.
/∗ in put 16 b i t i s convereted t o 32 b i t ∗/
arm_q15_to_q31 ( block −>data , q31_buf , AUDIO_BLOCK_SAMPLES) ;
/∗ e i g h t p a r a l l e l lowpass comb f i l t e r s ∗/
p r o c e s s _ l b c f (& l b c f [ 0 ] , q31_buf , sum_buf ) ;
Chapter 3. Implementation and Hardware Setup 72
/∗ f o u r cascaded a l l −pass f i l t e r s ∗/
p r o c e s s _ a p f (& apf [ 0 ] , sum_buf , q31_buf ) ;
p r o c e s s _ a p f (& apf [ 1 ] , q31_buf , q31_buf ) ;
p r o c e s s _ a p f (& apf [ 2 ] , q31_buf , q31_buf ) ;
p r o c e s s _ a p f (& apf [ 3 ] , q31_buf , q31_buf ) ;
/∗ f i l t e r output 32 b i t i s converted t o 16 b i t ∗/
arm_q31_to_q15 ( q31_buf , block −>data , AUDIO_BLOCK_SAMPLES) ;
73
Chapter 4
Evaluation
4.1 Latency
Definition Audio latency is the delay between sound being triggered and then actually
perceived. The reasons for the cause of latency in the audio system are ADCs, DACs,
buffering, digital signal transmission, transmission time, the speed of sound and the
transmission medium. Round-trip time (RTT), also called round-trip delay, is the time
required for a signal pulse or packet to travel from a specific source to a specific desti-
nation and back again. [48].
Evaluation:
The teensy library has audio objects works on the block of 128 samples of 16 bits per
sample. In the receiver interrupt the audio samples are copied when the receiver DMA
buffer is half filled and in the transmitter interrupt audio samples are sent out when
transmitter DMA buffer is half filled.
The round-trip time in the driver is Rx DMA half buffer delay + Tx DMA half buffer
delay. The receiver and transmitter DMA buffer of type 32 bits and it is not changed to
make it compatible with other configurations.
By using oscilloscope the latency can be measured. Oscilloscope captures the trans-
mitted and received signal, the figure 4.1 shows the delay between signal and it is
around 9ms.
Chapter 4. Evaluation 74
When recording audio into your computer, your sound card needs some time to pro-
cess the incoming information. The amount of time allotted for processing is called the
Buffer Size[49]. In general low buffer size is preferred as it limits the latency but some-
times it results in a higher burden on the system as it has very little time to process the
audio. When introducing the larger buffer size, the audio delay is generated. So it is
important to find the appropriate buffer size for the session as this can vary depending
on the number of tracks, plug-ins, audio files etc..
Evaluation:
During testing the DMA buffer size is increased from 128 to 256 samples per channel
and by using the oscilloscope the delay between input and the ouput signal is measured
(as shown in figure 4.2 ).The measured delay is around 18ms. As observed when the
DMA buffer size has increased the delay also increases. Smaller DMA size has less
latency but this will increase the interrupts and CPU load. Hence optimal DMA buffer
size must be chosen.
Chapter 4. Evaluation 75
DSP Benchmarks are measured for the DSP operations like FFT, DCT, FIR. They
typically measured in MIPS (Million instructions per second ) or DMIPS ( Dhrystone-
MIPS). For the ARM processor, DMIPS are used.
Evaluation
The CPU consumption and memory usage can be measured using the available
Teensy Audio library API’s listed below
• processorUsage
• processorUsageMax
• AudioMemoryUsage
• AudioMemoryUsageMax
These APIs calculates the number of CPU cycles taken for processing 128 samples
at the 44.1khz sample rate. The figure shows the CPU consumption and memory uti-
lization by I2S Driver and Freeverb Algorithm.
Chapter 4. Evaluation 76
• Kinetic DMA has very flexible control over the data width, data size, and minor
and major loops.
• DMA also generates interrupts at a regular interval (ping pong) during the trans-
fer of data. In this thesis, interrupt are generated when half of the buffer is trans-
ferred.
Evaluation Using multimeter the current and the voltage for the each component is
measured between the Vcc and GND in the full operating mode. The measured values
are summarized in the table 4.2
Chapter 5
Discussion / Limitations
During the study of this thesis, I did research on open source ecosystem. The open
source software has to be developed in time, must be more generic, it should be freely
available to all the programmer with non-restricted or limited restriction license and
must provide freedom to modify, review and redistribute the software or code. This
kind of approach is must faster and more efficient and results in better quality.
I got familiar with the open source operating system and their licenses. Did the ba-
sic study on a linux based platform like BeagleBoard, Raspberry Pi, Arduino Yún and
Intel Galileo. Also studied non-Linux based embedded devices like Arduino, Adafruit
Flora, LightBlue Bean.
During the course of the thesis, I have studied the teensy data specifications and
various generations of teensy hardwares. I found that teensy 3.6 very powerful micro-
controller and compatible with Arduino software and libraries. The open source Teensy
audio library is distributed under MIT-like license.
Through my literature study, I found out that the Teensy Audio library has support
for the control of various multi-channel audio codecs. All the codec are controlled us-
ing Inter-Integrated Circuit (I2C) bus and codecs are configured as Inter-IC Sound (I2S)
slave and teensy as Inter-IC Sound master with the maximum support of 8 channels in
Time-division multiplexing mode.
Since teensy audio library provides no support for the AD1938 codec, I have de-
veloped the AD1938 audio control class and it is derived from the base class "Audio-
Control" which is present in the audio library. This class has two function, SPI port
Chapter 5. Discussion / Limitations 79
control and AD1938 codec control. I have developed API’s of SPI read and write func-
tions for AD1938. The AD1938 codec contains 17 registers to configure Phase-locked
loops (PLL), Analog to digital converter (ADC), Digital to Analog converter (DAC). I
have configured these registers to achieve high-level function like configure, Enable,
Disable, Volume, Mute and Unmute.
To support daisy chain mode, I have developed multi-instance Serial Peripheral In-
terface(SPI) control class to configure two AD1938 codecs simultaneously.
Extensively studied Teensy audio library data flow and I2S input/ output classes
and also studied the MK66FX1M0VMD18 Kinetics processor control registers to con-
figure Direct memory access and Inter-IC Sound clocks.
The audio library of teensy contains object classes for receiving and transmitting
the audio. when teensy is master, this library has support for 8 input channels and 8
output channels. The two main objectives of this thesis are to make teensy as slave
and to connect two codecs and teensy in daisy chain mode as shown in figure 3.2 and
generate 8 input channel and 16 output channels. To achieve the above objectives, the
AduioOuputTDMSlave and AudioInputTDMSlave classes are developed which con-
figures clock, transmitter, receiver and enhanced Direct Memory Access. By using
Pulse-code modulation (PCM) pass through code and synthesized sine tone, the de-
veloped I2S driver is tested and verified for 8 input channels and 16 output channels.
In the second part of the thesis, the reverb effect is added to the teensy audio library.
During the study phase, many artificial digital reverberation algorithms like Schroeder,
Moorer, Gardner and others were studied. It is observed that the comb filters produce
the long echoes that occur between the walls. but parallel comb filter doesn’t produce
enough echo density as in realistic solution. To increase the echo density the paral-
lel comb filter output is fed to all-pass filters connected in series. These all-pass filter
multipliers the echoes. The low pass filter is added to the comb filter to simulate the fre-
quency dependent air absorption and increase echo density. The freeverb open source
algorithm, developed by Jezar has similar structure like Schroeder-Moorer reverbera-
tor. This algorithm has eight lowpass comb filters in parallel and four all-pass filters in
series.
The Freeverb Algorithm for Teensy is implemented using Fixed point arithmetics
so that the source code developed can be used with all teensy variants, which doesn’t
have floating processing unit. The fixed point implementation also reduces the CPU
computational and increases the precision for both left and right channels. The rever-
beration effect is analyzed by varying the feedback, damping factors of comb filter and
gain of the all-pass filter. The freeverb Algorithm is tested in real time by giving I2S
input and verified the effects by listening to the I2S output channel.
Chapter 5. Discussion / Limitations 80
Chapter 6
Conclusion
6.1 Conclusion
The main purpose of this thesis was to develop an audio driver for teensy using sound-
card CTAG 2/4 and implement the freeverb algorithm. The Audiocontrol class using
SPI drivers was developed to initialize and configure the AD1938 codec for various I2S
modes. This Audiocontrol class also supports volume control and mute. Analyzed the
existing I2S input/ output TDM class for the I2S master and developed the derived I2S
Input/output TDM in slave mode. Later, this class was extended to support 16 input
channels and 16 output channels. By understanding the AD1938 control registers, two
soundcards are connected in daisy chain mode which supports 8 input audio channels
and 16 audio output channels. The I2S driver latency is measured using an oscillo-
scope as 10.66 ms. A PCM pass-through code with existing audio library objects is
created and tested. As the system supports only 8 input channels, internally generated
sine tone was used to verify the other output channels. After the PCM pass through,
this thesis added reverb effect using the open source freeverb algorithm in fixed-point
arithmetic for the fixed delay length. This algorithm is tested in the real time by giving
I2s audio input and varied the reverberation effects.
As a future extension one can adjust the delay length to increase the reverberation
time. In the current implementation, the internal delay buffers are of size 32 bits width,
the buffer width can be carefully optimized to 16 bit. There is a scope for the optimiza-
tion in the interrupt service routine(ISR) of transmitter and receiver.
82
Bibliography
[1] Understanding and implementation of open source ecosystems final. URL: https : / /
www.slideshare.net/RachitTechnologyPvtL/understanding-and-implementation-
of-open-source-ecosystems-final.
[2] The Open Source Definition. URL: https://opensource.org/osd-annotated.
[3] Raspberry pi model B. URL: https://www.raspberrypi.org/products/raspberry-
pi-3-model-b/.
[4] Arduino Yún LininoOS. URL: https://www.arduino.cc/en/Main/ArduinoBoardYun?
from=Main.ArduinoYUN.
[5] Intel Galileo. URL: https://www.arduino.cc/en/ArduinoCertified/IntelGalileo.
[6] FLORA - Wearable electronic platform: Arduino-compatible - v3. URL: https://www.
adafruit.com/product/659.
[7] LightBlue Bean. URL: https://www.adafruit.com/product/2732.
[8] PlatformIO. Teensy. Revision 9fc5aecb, 2014. URL: http://docs.platformio.org/
en/latest/platforms/teensy.html.
[9] Teensy USB Development Board. URL: https://www.pjrc.com/teensy/.
[10] Tommaso Melodia G. Enrico Santagati. “A Software-Defined Ultrasonic Network-
ing Framework for Wearable Devices”. In: IEEE/ACM TRANSACTIONS ON NET-
WORKING (2016).
[11] Audio Connections and Memory. URL: https://www.pjrc.com/teensy/td_libs_
AudioConnection.html.
[12] Philips Semiconductors. I2S bus specification. High Tech Campus 60 5656 AG Eind-
hoven, NoordBrabant, The Netherlands., june 5, 1996 edition. URL: https://www.
sparkfun.com/datasheets/BreakoutBoards/I2SBUS.pdf.
[13] Cirrus Logic. Time Division Multiplexed Audio Interface. 2006. URL: https://d3uzseaevmutz1.
cloudfront.net/pubs/appNote/AN301REV1.pdf.
[14] Corelis An EWA Company. Serial Peripheral Interface (SPI) Bus. URL: https : / /
www.corelis.com/whitepapers/BusPro-S_SPI_Tutorial.pdf.
[15] Leens F. “An introduction to I2C and SPI protocols”. In: IEEE Instrumentation &
Measurement Magazine (2009).
[16] Direct Memory Access (DMA) Modes and Bus Mastering DMA. URL: http://www.
pcguide.com/ref/hdd/if/ide/modesDMA-c.html.
BIBLIOGRAPHY 83
Appendix A
Appendix
Control_ad1938.h
# i f n d e f _CONTROL_AD1938_H_
# d e f i n e _CONTROL_AD1938_H_
/∗ SPI ∗/
# d e f i n e AD1938_SPI_CLK_FREQ 1000000
# d e f i n e AD1938_SPI_CHIP_SEL 7 /∗ t e e n s y 3 . 6 gpio ∗/
# d e f i n e AD1938_SPI_SCK 14 /∗ t e e n s y 3 . 6 gpio ∗/
# d e f i n e AD1938_RESET 17 /∗ t e e n s y 3 . 6 gpio ∗/
/∗ sampling r a t e ∗/
t y p e d e f enum
{
FS_32000 ,
FS_44100 ,
FS_48000 ,
FS_64000 ,
FS_88200 ,
FS_96000 ,
FS_128000 ,
FS_176400 ,
FS_192000 ,
} Te_samplingRate ;
/∗ I 2 s c l o c k mode∗/
t y p e d e f enum
{
AD1938_I2S_SLAVE ,
AD1938_I2S_MASTER ,
} Te_i2sClockMode ;
/∗ I 2 s number o f c h a n n e l s ∗/
t y p e d e f enum
{
I2S_STEREO_2CH ,
I2S_TDM_8CH ,
I2S_TDM_16CH
} Te_i2sNumChannels ;
c l a s s AudioControlAD1938 : p u b l i c AudioControl
{
public :
bool s p i I n i t ( i n t c l a t c h , i n t r e s e t , i n t cout , i n t cin , i n t c c l k ) ;
bool c o n f i g ( Te_samplingRate sampleRate , Te_bitsPerSample wordLen ,
Te_i2sNumChannels numChannels , Te_i2sClockMode mode ) ;
bool e n a b l e ( void ) ;
Appendix A. Appendix 88
bool d i s a b l e ( void ) ;
bool volume ( f l o a t volume ) ;
bool adcMute ( bool mute ) ;
bool dacMute ( bool mute ) ;
void r e a d A l l r e g ( void ) ;
bool inputSelect ( int n) {
r e t u r n ( n == 0 ) ? t r u e : f a l s e ;
}
bool i n p u t L e v e l ( f l o a t volume ) {
return f a l s e ;
}
private :
i n t ad1938_clatch ;
i n t ad1938_reset ;
i n t ad1938_cout ;
i n t ad1938_cin ;
i n t ad1938_cclk ;
Te_i2sClockMode i2sMode ;
Te_bitsPerSample wordLen ;
Te_i2sNumChannels numChannels ;
Te_samplingRate samplingRate ;
protected :
bool s p i _ w r i t e _ r e g ( unsigned char reg , unsigned char v a l ) ;
unsigned char s p i _ r e a d _ r e g ( unsigned char reg ) ;
bool i s P l l L o c k e d ( ) ;
bool dacVolume ( i n t dac_num , i n t volume ) ;
};
# e n d i f // ! _CONTROL_AD1938_H_
control_ad1938.cpp
/∗ AD1938 Audio Codec c o n t r o l l i b r a r y
∗
∗ Copyright ( c ) 2 0 1 7 , Yasmeen S u l t a n a
∗
∗
∗ Permission i s hereby granted , f r e e o f charge , t o any person o b t a i n i n g a
copy
∗ o f t h i s s o f t w a r e and a s s o c i a t e d documentation f i l e s ( t h e " Software " ) , t o
deal
∗ i n t h e Software without r e s t r i c t i o n , i n c l u d i n g without l i m i t a t i o n t h e
rights
∗ t o use , copy , modify , merge , publish , d i s t r i b u t e , s u b l i c e n s e , and/or s e l l
∗ c o p i e s o f t h e Software , and t o permit persons t o whom t h e Software i s
∗ f u r n i s h e d t o do so , s u b j e c t t o t h e f o l l o w i n g c o n d i t i o n s :
∗
∗ The above c o p y r i g h t n o t i c e , development funding n o t i c e , and t h i s permission
∗ n o t i c e s h a l l be included i n a l l c o p i e s or s u b s t a n t i a l p o r t i o n s o f t h e
Software .
∗
Appendix A. Appendix 89
∗ THE SOFTWARE I S PROVIDED "AS I S " , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
∗ IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
∗ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
∗ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
∗ LIABILITY , WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
∗ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
∗ THE SOFTWARE.
∗/
# include " control_ad1938 . h"
# i n c l u d e <SPI . h>
/∗
reference
h t t p ://www. analog . com/media/en/ t e c h n i c a l −documentation/data −s h e e t s /AD1938 . pdf
h t t p ://www. analog . com/media/en/ t e c h n i c a l −documentation/ a p p l i c a t i o n −n o t e s /AN
− 1365. pdf
∗/
/∗ SPI 3 byte r e g i s t e r format
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
|Global Address |R/W | R e g i s t e r Address |Data |
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
|23:17 |16 | 1 5 : 8 | 7:0|
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
Address R e g i s t e r
0 PLL and Clock Co n tr o l 0
1 PLL and Clock Co n tr o l 1
2 DAC Co n tr o l 0
3 DAC Co n tr o l 1
4 DAC Co n tr o l 2
5 DAC i n d i v i d u a l channel mutes
6 DAC L1 volume c o n t r o l
7 DAC R1 volume c o n t r o l
8 DAC L2 volume c o n t r o l
9 DAC R2 volume c o n t r o l
10 DAC L3 volume c o n t r o l
11 DAC R3 volume c o n t r o l
12 DAC L4 volume c o n t r o l
13 DAC R4 volume c o n t r o l
14 ADC C on t ro l 0
15 ADC C on t ro l 1
16 ADC C on t ro l 2
∗/
# d e f i n e AD1938_SPI_WRITE_BYTE_COUNT 3
# d e f i n e AD1938_SPI_READ_BYTE_COUNT 2
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
/∗PLL and Clock C on t ro l 0
∗/
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
# d e f i n e AD1938_PLL_CLK_CTRL0 0 x00
/∗
B i t Value Function D e s c r i p t i o n
0 0 Normal o p e r a t i o n PLL power−down
1 Power−down
2 : 1 00 INPUT 256 ( 4 4 . 1 kHz or 48 kHz ) MCLKI/XI pin f u n c t i o n a l i t y ( PLL a c t i v e
) , master c l o c k r a t e s e t t i n g
01 INPUT 384 ( 4 4 . 1 kHz or 48 kHz )
10 INPUT 512 ( 4 4 . 1 kHz or 48 kHz )
11 INPUT 768 ( 4 4 . 1 kHz or 48 kHz )
4 : 3 00 XTAL o s c i l l a t o r enabled MCLKO/XO pin , master c l o c k r a t e s e t t i n g
01 256 ∗ f S VCO output
10 512 ∗ f S VCO output
11 Off
6 : 5 00 MCLKI/XI PLL input
01 DLRCLK
10 ALRCLK
11 Reserved
7 0 D i s a b l e : ADC and DAC i d l e I n t e r n a l master c l o c k e n a b l e
1 Enable : ADC and DAC a c t i v e
∗/
# d e f i n e DIS_ADC_DAC ( 0 x00 )
# d e f i n e ENA_ADC_DAC ( 0 x80 )
# d e f i n e PLL_IN_MCLK ( 0 x00 )
# d e f i n e PLL_IN_DLRCLK ( 0 x20 )
# d e f i n e PLL_IN_ALRCLK ( 0 x40 )
# d e f i n e PLL_PWR_UP ( 0 x00 )
# d e f i n e PLL_PWR_DWN ( 0 x01 )
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
/∗PLL and Clock C on t ro l 1
∗/
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
# d e f i n e AD1938_PLL_CLK_CTRL1 0 x01
/∗
B i t Value Function D e s c r i p t i o n
0 0 PLL c l o c k DAC c l o c k s o u r c e s e l e c t
1 MCLK
1 0 PLL c l o c k ADC c l o c k s o u r c e s e l e c t
1 MCLK
2 0 Enabled On−chip v o l t a g e r e f e r e n c e
1 Disabled
3 0 Not locked PLL l o c k i n d i c a t o r ( read −only )
1 Locked
7 : 4 0000 Reserved
∗/
# d e f i n e AD1938_PLL_LOCK ( 0 x08 )
# d e f i n e DIS_VREF ( 0 x04 )
# d e f i n e ENA_VREF ( 0 x00 )
# d e f i n e ADC_CLK_PLL ( 0 x00 )
# d e f i n e ADC_CLK_MCLK ( 0 x20 )
# d e f i n e DAC_CLK_PLL ( 0 x00 )
# d e f i n e DAC_CLK_MCLK ( 0 x01 )
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
/∗ DAC C on tr o l 0
∗/
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
# d e f i n e AD1938_DAC_CTRL0 0 x02
/∗
B i t Value Function D e s c r i p t i o n
0 0 Normal Power−down
1 Power−down
2 : 1 00 32 kHz/ 4 4 . 1 kHz/48 kHz Sample r a t e
01 64 kHz/ 8 8 . 2 kHz/96 kHz
10 128 kHz/ 1 7 6 . 4 kHz/192 kHz
11 Reserved
Appendix A. Appendix 92
# d e f i n e DAC_SR_48K ( 0 x00 )
# d e f i n e DAC_SR_96K ( 0 x02 )
# d e f i n e DAC_SR_192K ( 0 x04 )
# d e f i n e DAC_PWR_UP ( 0 x00 )
# d e f i n e DAC_PWR_DWN ( 0 x01 )
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
/∗ DAC C on tr o l 1
∗/
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
# d e f i n e AD1938_DAC_CTRL1 0 x03
/∗
B i t Value Function D e s c r i p t i o n
0 0 Latch i n mid c y c l e ( normal ) BCLK a c t i v e edge (TDM i n )
1 Latch i n a t end o f c y c l e ( p i p e l i n e )
2 : 1 00 64 ( 2 c h a n n e l s ) BCLKs per frame
01 128 ( 4 c h a n n e l s )
10 256 ( 8 c h a n n e l s )
11 512 ( 1 6 c h a n n e l s )
3 0 L e f t low LRCLK p o l a r i t y
1 L e f t high
4 0 S l a v e LRCLK master/ s l a v e
Appendix A. Appendix 93
1 Master
5 0 S l a v e BCLK master/ s l a v e
1 Master
6 0 DBCLK pin BCLK s o u r c e
1 I n t e r n a l l y generated
7 0 Normal BCLK p o l a r i t y
1 Inverted
∗/
# d e f i n e DAC_BCLK_POL_NORM ( 0 x00 )
# d e f i n e DAC_BCLK_POL_INV ( 0 x80 )
# d e f i n e DAC_BCLK_SRC_PIN ( 0 x00 )
# d e f i n e DAC_BCLK_SRC_INTERNAL ( 0 x40 )
# d e f i n e DAC_BCLK_SLAVE ( 0 x00 )
# d e f i n e DAC_BCLK_MASTER ( 0 x20 )
# d e f i n e DAC_LRCLK_SLAVE ( 0 x00 )
# d e f i n e DAC_LRCLK_MASTER ( 0 x10 )
# d e f i n e DAC_LRCLK_POL_NORM ( 0 x00 )
# d e f i n e DAC_LRCLK_POL_INV ( 0 x08 )
# d e f i n e DAC_LATCH_MID ( 0 x00 )
# d e f i n e DAC_LATCH_END ( 0 x01 )
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
/∗ DAC C on tr o l 2
∗/
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
/∗
B i t Value Function D e s c r i p t i o n
0 0 Unmute Master mute
1 Mute
2 : 1 00 F l a t De−emphasis ( 3 2 kHz/ 4 4 . 1 kHz/48 kHz mode only )
01 48 kHz curve
10 4 4 . 1 kHz curve
11 32 kHz curve
4 : 3 00 24 Word width
01 20
10 Reserved
11 16
5 0 Noninverted DAC output p o l a r i t y
Appendix A. Appendix 94
1 Inverted
7 : 6 00 Reserved
∗/
# d e f i n e AD1938_DAC_CTRL2 0 x04
# d e f i n e DAC_OUT_POL_NORM ( 0 x00 )
# d e f i n e DAC_OUT_POL_INV ( 0 x20 )
# d e f i n e DAC_WIDTH_24 ( 0 x00 )
# d e f i n e DAC_WIDTH_20 ( 0 x08 )
# d e f i n e DAC_WIDTH_16 ( 0 x18 )
# d e f i n e DAC_UNMUTE_ALL ( 0 x00 )
# d e f i n e DAC_MUTE_ALL ( 0 x01 )
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
/∗ DAC i n d i v i d u a l channel mutes
∗/
/∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
∗/
# d e f i n e AD1938_DAC_CHNL_MUTE 0 x05
/∗
B i t Value Function D e s c r i p t i o n
0 0 Unmute DAC 1 l e f t mute
1 Mute
1 0 Unmute DAC 1 r i g h t mute
1 Mute
2 0 Unmute DAC 2 l e f t mute
1 Mute
3 0 Unmute DAC 2 r i g h t mute
1 Mute
4 0 Unmute DAC 3 l e f t mute
1 Mute
5 0 Unmute DAC 3 r i g h t mute
1 Mute
6 0 Unmute DAC 4 l e f t mute
1 Mute
7 0 Unmute DAC 4 r i g h t mute
1 Mute
∗/
# d e f i n e DACMUTE_R4 ( 0 x80 )
# d e f i n e DACMUTE_L4 ( 0 x40 )
# d e f i n e DACMUTE_R3 ( 0 x20 )
# d e f i n e DACMUTE_L3 ( 0 x10 )
# d e f i n e DACMUTE_R2 ( 0 x08 )
# d e f i n e DACMUTE_L2 ( 0 x04 )
Appendix A. Appendix 95
# d e f i n e DACMUTE_R1 ( 0 x02 )
# d e f i n e DACMUTE_L1 ( 0 x01 )
/∗
B i t Value Function D e s c r i p t i o n
7:0 0 No a t t e n u a t i o n DAC volume c o n t r o l
1 t o 254 −3/8 dB per s t e p
255 Full attenuation
∗/
# d e f i n e DACVOL_MIN ( 0 xFF )
# d e f i n e DACVOL_LOW ( 0 xC0 )
# d e f i n e DACVOL_MED ( 0 x80 )
# d e f i n e DACVOL_HI ( 0 x40 )
# d e f i n e DACVOL_MAX ( 0 x00 ) // 0db Volume
# d e f i n e DACVOL_MASK ( 0 xFF )
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗DAC L1 volume c o n t r o l ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_DAC_L1_VOL 0 x06
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗DAC R1 volume c o n t r o l ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_DAC_R1_VOL 0 x07
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗DAC L2 volume c o n t r o l ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_DAC_L2_VOL 0 x08
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗DAC R2 volume c o n t r o l
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_DAC_R2_VOL 0 x09
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗DAC L3 volume c o n t r o l
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_DAC_L3_VOL 0 x0a
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗DAC R3 volume c o n t r o l
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_DAC_R3_VOL 0 x0b
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗DAC L4 volume c o n t r o l
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_DAC_L4_VOL 0 x0c
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
Appendix A. Appendix 96
/∗DAC R4 volume c o n t r o l
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_DAC_R4_VOL 0x0d
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ ADC C on tr o l 0 ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_ADC_CTRL0 0 x0e
/∗
B i t Value Function D e s c r i p t i o n
0 0 Normal Power−down
1 Power down
1 0 Off High−pass f i l t e r
1 On
2 0 Unmute ADC L1 mute
1 Mute
3 0 Unmute ADC R1 mute
1 Mute
4 0 Unmute ADC L2 mute
1 Mute
5 0 Unmute ADC R2 mute
1 Mute
7 : 6 00 32 kHz/ 4 4 . 1 kHz/48 kHz Output sample r a t e
01 64 kHz/ 8 8 . 2 kHz/96 kHz
10 128 kHz/ 1 7 6 . 4 kHz/192 kHz
11 Reserved
∗/
# d e f i n e ADC_SR_48K ( 0 x00 )
# d e f i n e ADC_SR_96K ( 0 x40 )
# d e f i n e ADC_SR_192K ( 0 x80 )
# d e f i n e ADC_R2_UNMUTE ( 0 x00 )
# d e f i n e ADC_R2_MUTE ( 0 x20 )
# d e f i n e ADC_L2_UNMUTE ( 0 x00 )
# d e f i n e ADC_L2_MUTE ( 0 x10 )
# d e f i n e ADC_R1_UNMUTE ( 0 x00 )
# d e f i n e ADC_R1_MUTE ( 0 x08 )
# d e f i n e ADC_L1_UNMUTE ( 0 x00 )
# d e f i n e ADC_L1_MUTE ( 0 x04 )
# d e f i n e ADC_HP_FILT_OFF ( 0 x00 )
# d e f i n e ADC_HP_FILT_ON ( 0 x02 )
# d e f i n e ADC_PWR_UP ( 0 x00 )
# d e f i n e ADC_PWR_DWN ( 0 x01 )
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ ADC C on tr o l 01 ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
Appendix A. Appendix 97
# d e f i n e AD1938_ADC_CTRL1 0 x0f
/∗
B i t Value Function D e s c r i p t i o n
1 : 0 00 24 Word width
01 20
10 Reserved
11 16
4 : 2 000 1 SDATA delay (BCLK p e r i o d s )
001 0
010 8
011 12
100 16
101 Reserved
110 Reserved
111 Reserved
6 : 5 00 S t e r e o S e r i a l format
01 TDM ( d a i s y chain )
10 ADC AUX mode (ADC− , DAC− , TDM−coupled )
11 Reserved
7 0 Latch i n mid c y c l e ( normal ) BCLK a c t i v e edge (TDM i n )
1 Latch i n a t end o f c y c l e ( p i p e l i n e )
∗/
# d e f i n e ADC_LATCH_MID ( 0 x00 )
# d e f i n e ADC_LATCH_END ( 0 x80 )
# d e f i n e ADC_FMT_I2S ( 0 x00 )
# d e f i n e ADC_FMT_TDM ( 0 x20 )
# d e f i n e ADC_FMT_AUX ( 0 x40 )
# d e f i n e ADC_WIDTH_24 ( 0 x00 )
# d e f i n e ADC_WIDTH_20 ( 0 x01 )
# d e f i n e ADC_WIDTH_16 ( 0 x03 )
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ ADC C on tr o l 2 ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
# d e f i n e AD1938_ADC_CTRL2 0 x10
/∗
B i t Value Function D e s c r i p t i o n
0 0 50/50 ( a l l o w s 3 2 , 2 4 , 2 0 , or 16 b i t c l o c k s ( BCLKs ) per channel ) LRCLK
format
1 P ulse ( 3 2 BCLKs per channel )
1 0 Drive out on f a l l i n g edge (DEF) BCLK p o l a r i t y
1 Drive out on r i s i n g edge
2 0 L e f t low LRCLK p o l a r i t y
1 L e f t high
3 0 S l a v e LRCLK master/ s l a v e
Appendix A. Appendix 98
1 Master
5 : 4 00 64 BCLKs per frame
01 128
10 256
11 512
6 0 S l a v e BCLK master/ s l a v e
1 Master
7 0 ABCLK pin BCLK s o u r c e
1 I n t e r n a l l y generated
∗/
# d e f i n e ADC_BCLK_SRC_PIN ( 0 x00 )
# d e f i n e ADC_BCLK_SRC_INTERNAL ( 0 x80 )
# d e f i n e ADC_BCLK_SLAVE ( 0 x00 )
# d e f i n e ADC_BCLK_MASTER ( 0 x40 )
# d e f i n e ADC_LRCLK_SLAVE ( 0 x00 )
# d e f i n e ADC_LRCLK_MASTER ( 0 x08 )
# d e f i n e ADC_LRCLK_POL_NORM ( 0 x00 )
# d e f i n e ADC_LRCLK_POL_INV ( 0 x04 )
# d e f i n e ADC_BCLK_POL_NORM ( 0 x00 )
# d e f i n e ADC_BCLK_POL_INV ( 0 x02 )
# d e f i n e ADC_LRCLK_FMT_50_50 ( 0 x00 )
# d e f i n e ADC_LRCLK_FMT_PULSE ( 0 x01 )
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ i n i t ( void ) ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : s p i I n i t ( i n t c l a t c h , i n t r e s e t , i n t cout , i n t cin , i n t
cclk )
{
ad1938_clatch = clatch ;
ad1938_reset = r e s e t ;
ad1938_cout = cout ;
ad1938_cin = cin ;
ad1938_cclk = cclk ;
/∗∗/
pinMode ( a d 1 9 3 8 _ c l a t c h , OUTPUT) ;
pinMode ( a d 1 9 3 8 _ r e s e t , OUTPUT) ;
/∗ SPI c l o c k pin s e t ∗/
// SPI . setMOSI ( c i n ) ;
Appendix A. Appendix 99
return true ;
}
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ spi_read_reg ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
unsigned char AudioControlAD1938 : : s p i _ r e a d _ r e g ( unsigned char reg )
{
unsigned char r e s u l t = 0 ;
unsigned char data [ AD1938_SPI_WRITE_BYTE_COUNT ] ;
data [ 0 ] = AD1938_READ_ADDRESS ;
data [ 1 ] = reg ;
data [ 2 ] = 0 x0 ;
// and c o n f i g u r e s e t t i n g s
SPI . b e g i n T r a n s a c t i o n ( S P I S e t t i n g s ( AD1938_SPI_CLK_FREQ , MSBFIRST ,
SPI_MODE3 ) ) ;
// t a k e t h e chip s e l e c t low t o s e l e c t t h e d e v i c e :
d i g i t a l W r i t e ( a d 1 9 3 8 _ c l a t c h , LOW) ;
return ( r e s u l t ) ;
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ spi_write_reg
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : s p i _ w r i t e _ r e g ( unsigned char reg , unsigned char v a l )
{
Appendix A. Appendix 100
// and c o n f i g u r e s e t t i n g s
SPI . b e g i n T r a n s a c t i o n ( S P I S e t t i n g s ( AD1938_SPI_CLK_FREQ , MSBFIRST ,
SPI_MODE3 ) ) ;
// t a k e t h e chip s e l e c t low t o s e l e c t t h e d e v i c e :
d i g i t a l W r i t e ( a d 1 9 3 8 _ c l a t c h , LOW) ;
return true ;
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ i n i t ( void )
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : c o n f i g ( Te_samplingRate sampleRate ,
Te_bitsPerSample wordLen ,
Te_i2sNumChannels
numChannels
,
Te_i2sClockMode
mode )
{
unsigned char d a c _ f s = 0 ;
unsigned char a d c _ f s = 0 ;
unsigned char dac_mode = 0 ;
unsigned char adc_mode = 0 ;
unsigned char dac_wl = 0 ;
unsigned char adc_wl = 0 ;
unsigned char dac_channels = 0 ;
unsigned char adc_channels = 0 ;
i2sMode = mode ;
wordLen = wordLen ;
numChannels = numChannels ;
samplingRate = sampleRate ;
Appendix A. Appendix 101
switch ( sampleRate )
{
c a s e FS_32000 :
c a s e FS_44100 :
c a s e FS_48000 :
{
d a c _ f s = DAC_SR_48K ;
a d c _ f s = ADC_SR_48K ;
}
break ;
c a s e FS_64000 :
c a s e FS_88200 :
c a s e FS_96000 :
{
d a c _ f s = DAC_SR_96K ;
a d c _ f s = ADC_SR_96K ;
}
break ;
c a s e FS_128000 :
c a s e FS_176400 :
c a s e FS_192000 :
{
d a c _ f s = DAC_SR_192K ;
a d c _ f s = ADC_SR_192K ;
}
break ;
default :
{
d a c _ f s = DAC_SR_48K ;
a d c _ f s = ADC_SR_48K ;
}
}
switch ( wordLen )
{
c a s e BITS_16 :
{
dac_wl = DAC_WIDTH_16 ;
adc_wl = ADC_WIDTH_16 ;
}
break ;
c a s e BITS_20 :
{
dac_wl = DAC_WIDTH_20 ;
adc_wl = ADC_WIDTH_20 ;
}
break ;
c a s e BITS_24 :
{
Appendix A. Appendix 102
dac_wl = DAC_WIDTH_24 ;
adc_wl = ADC_WIDTH_24 ;
break ;
default :
{
dac_wl = DAC_WIDTH_24 ;
adc_wl = ADC_WIDTH_24 ;
}
}
switch ( numChannels )
{
c a s e I2S_STEREO_2CH :
{
dac_mode = DAC_FMT_I2S ;
adc_mode = ADC_FMT_I2S ;
dac_channels = DAC_CHANNELS_2 ;
adc_channels = ADC_CHANNELS_2 ;
}
break ;
c a s e I2S_TDM_8CH :
{
dac_mode = DAC_FMT_TDM;
adc_mode = ADC_FMT_TDM;
dac_channels = DAC_CHANNELS_8 ;
adc_channels = ADC_CHANNELS_8 ;
}
break ;
c a s e I2S_TDM_16CH :
{
dac_mode = DAC_FMT_TDM;
adc_mode = ADC_FMT_TDM;
dac_channels = DAC_CHANNELS_16 ;
adc_channels = ADC_CHANNELS_16 ;
}
break ;
default :
{
dac_mode = DAC_FMT_I2S ;
adc_mode = ADC_FMT_I2S ;
dac_channels = DAC_CHANNELS_2 ;
adc_channels = ADC_CHANNELS_2 ;
}
}
Appendix A. Appendix 103
i f ( mode == AD1938_I2S_SLAVE )
{
//2 DAC Co n tr ol 0
s p i _ w r i t e _ r e g ( AD1938_DAC_CTRL0 , ( dac_mode | DAC_BCLK_DLY_1 | d a c _ f s |
DAC_PWR_UP) ) ;
//3 DAC Co n tr ol 1
s p i _ w r i t e _ r e g ( AD1938_DAC_CTRL1 , ( DAC_BCLK_SRC_PIN|DAC_BCLK_SLAVE|
DAC_LRCLK_SLAVE |DAC_LRCLK_POL_NORM | dac_channels |
DAC_LATCH_MID) ) ;
//4 DAC Co n tr ol 2
s p i _ w r i t e _ r e g ( AD1938_DAC_CTRL2 , dac_wl ) ;
//14 ADC Co n tr ol 0
s p i _ w r i t e _ r e g ( AD1938_ADC_CTRL0 , a d c _ f s ) ;
//15 ADC Co n tr ol 1
s p i _ w r i t e _ r e g ( AD1938_ADC_CTRL1 , (ADC_LATCH_MID | adc_mode |
ADC_BCLK_DLY_0 | adc_wl ) ) ;
//16 ADC Co n tr ol 2
s p i _ w r i t e _ r e g ( AD1938_ADC_CTRL2 , ( ADC_BCLK_SRC_PIN|ADC_BCLK_SLAVE |
adc_channels | ADC_LRCLK_SLAVE | ADC_LRCLK_FMT_50_50|
ADC_LRCLK_POL_NORM|ADC_BCLK_POL_NORM) ) ;
}
else
{
//0 PLL and Clock Co n tr o l 0
s p i _ w r i t e _ r e g ( AD1938_PLL_CLK_CTRL0 , (DIS_ADC_DAC | INPUT512 |
PLL_IN_MCLK | MCLK_OUT_XTAL |PLL_PWR_DWN) ) ;
//2 DAC Co n tr ol 0
s p i _ w r i t e _ r e g ( AD1938_DAC_CTRL0 , ( dac_mode | DAC_BCLK_DLY_1 |
d a c _ f s | DAC_PWR_UP) ) ;
//3 DAC Co n tr ol 1
s p i _ w r i t e _ r e g ( AD1938_DAC_CTRL1 , ( DAC_BCLK_SRC_INTERNAL|
DAC_BCLK_SLAVE| DAC_LRCLK_SLAVE |DAC_LRCLK_POL_NORM |
dac_channels | DAC_LATCH_MID) ) ;
//4 DAC Co n tr ol 2
s p i _ w r i t e _ r e g ( AD1938_DAC_CTRL2 , dac_wl ) ;
//14 ADC Co n tr ol 0
s p i _ w r i t e _ r e g ( AD1938_ADC_CTRL0 , a d c _ f s ) ;
//15 ADC Co n tr ol 1
s p i _ w r i t e _ r e g ( AD1938_ADC_CTRL1 , (ADC_LATCH_MID | adc_mode |
ADC_BCLK_DLY_1 | adc_wl ) ) ;
//16 ADC Co n tr ol 2
s p i _ w r i t e _ r e g ( AD1938_ADC_CTRL2 , ( ADC_BCLK_SRC_INTERNAL|
ADC_BCLK_MASTER | adc_channels | ADC_LRCLK_MASTER |
ADC_LRCLK_FMT_50_50|ADC_LRCLK_POL_NORM|ADC_BCLK_POL_NORM)
);
}
return true ;
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ i s P l l L o c k e d ( void )
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : i s P l l L o c k e d ( void )
{
r e t u r n ( ( s p i _ r e a d _ r e g ( AD1938_PLL_CLK_CTRL1 ) > >3)&0x1 ) ;
}
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ e n a b l e ( void )
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : e n a b l e ( void )
{
i f ( i2sMode == AD1938_I2S_SLAVE )
{
s p i _ w r i t e _ r e g ( AD1938_PLL_CLK_CTRL0 , (ENA_ADC_DAC | INPUT512 |
PLL_IN_DLRCLK | MCLK_OUT_OFF | PLL_PWR_UP) ) ;
}
else
{
s p i _ w r i t e _ r e g ( AD1938_PLL_CLK_CTRL0 , (ENA_ADC_DAC | INPUT512 |
PLL_IN_MCLK | MCLK_OUT_XTAL |PLL_PWR_UP) ) ;
Appendix A. Appendix 106
return true ;
}
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ d i s a b l e ( void )
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : d i s a b l e ( void )
{
unsigned char r e g _ v a l u e ;
r e g _ v a l u e = s p i _ r e a d _ r e g ( AD1938_PLL_CLK_CTRL0 ) ;
r e g _ v a l u e = ( r e g _ v a l u e&0x7e ) ; /∗mask t h e l a s t and f i r s t b i t s ∗/
s p i _ w r i t e _ r e g ( AD1938_PLL_CLK_CTRL0 , (DIS_ADC_DAC | r e g _ v a l u e|
PLL_PWR_DWN) ) ;
r e g _ v a l u e = s p i _ r e a d _ r e g ( AD1938_DAC_CTRL0 ) ;
r e g _ v a l u e = s p i _ r e a d _ r e g ( AD1938_ADC_CTRL0 ) ;
return true ;
}
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ dacVolume ( i n t dac_num , f l o a t volume )
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : dacVolume ( i n t dac_num , i n t volume )
{
switch ( dac_num )
{
c a s e 0 : //DAC0
s p i _ w r i t e _ r e g ( AD1938_DAC_L1_VOL , volume ) ;
s p i _ w r i t e _ r e g ( AD1938_DAC_R1_VOL , volume ) ;
break ;
c a s e 1 : //DAC1
s p i _ w r i t e _ r e g ( AD1938_DAC_L2_VOL , volume ) ;
s p i _ w r i t e _ r e g ( AD1938_DAC_R2_VOL , volume ) ;
break ;
c a s e 2 : //DAC2
s p i _ w r i t e _ r e g ( AD1938_DAC_L3_VOL , volume ) ;
s p i _ w r i t e _ r e g ( AD1938_DAC_R3_VOL , volume ) ;
break ;
c a s e 3 : //DAC3
s p i _ w r i t e _ r e g ( AD1938_DAC_L4_VOL , volume ) ;
Appendix A. Appendix 107
s p i _ w r i t e _ r e g ( AD1938_DAC_R4_VOL , volume ) ;
break ;
}
return true ;
}
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ volume ( f l o a t volume ) ∗/ /∗
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : volume ( f l o a t volume )
{
i n t vol = 0 ;
vol =( i n t ) ( ( 1 . 0 − volume ) ∗ 2 5 5 ) ;
i f ( vol <0)
vol = 0 ;
i f ( vol >255)
vol = 2 5 5 ;
dacVolume ( 0 , vol ) ;
dacVolume ( 1 , vol ) ;
dacVolume ( 2 , vol ) ;
dacVolume ( 3 , vol ) ;
return true ;
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ dacMute
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : dacMute ( bool mute )
{
i f ( mute == t r u e )
{
s p i _ w r i t e _ r e g (AD1938_DAC_CHNL_MUTE, 0 x f f ) ; /∗mute∗/
}
else
{
s p i _ w r i t e _ r e g (AD1938_DAC_CHNL_MUTE, 0 0 ) ; /∗unmute∗/
}
return true ;
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ adcMute
∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
bool AudioControlAD1938 : : adcMute ( bool mute )
Appendix A. Appendix 108
{
unsigned char r e g _ v a l u e ;
r e g _ v a l u e = s p i _ r e a d _ r e g ( AD1938_ADC_CTRL0 ) ;
i f ( mute == t r u e )
{
s p i _ w r i t e _ r e g ( AD1938_ADC_CTRL0 , ( r e g _ v a l u e&0xc3 ) |0 x3c ) ; /∗mute
∗/
}
else
{
s p i _ w r i t e _ r e g ( AD1938_ADC_CTRL0 , ( r e g _ v a l u e&0xc3 ) ) ; /∗unmute∗/
}
return true ;
}
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ readAllreg ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
void AudioControlAD1938 : : r e a d A l l r e g ( void )
{
i n t i =0;
unsigned char r e g _ v a l = 0 ;
S e r i a l . p r i n t ( " \n r e a d A l l r e g \n " ) ;
f o r ( i = 0 ; i < 1 7 ; i ++)
{
reg_val = spi_read_reg ( i ) ;
S e r i a l . p r i n t ( " \n " ) ;
Serial . print ( i ) ;
S e r i a l . print ( "\t " ) ;
S e r i a l . p r i n t ( r e g _ v a l , HEX) ;
}
}
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
/∗ end o f f i l e ∗/
/∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗/
input_tdm.h
/∗ Audio L i b r a r y f o r Teensy 3 . X
∗ Copyright ( c ) 2 0 1 7 , Paul S t o f f r e g e n , paul@pjrc . com
∗
∗ Development o f t h i s audio l i b r a r y was funded by PJRC .COM, LLC by s a l e s o f
∗ Teensy and Audio Adaptor boards . P l e a s e support PJRC ’ s e f f o r t s t o develop
∗ open s o u r c e s o f t w a r e by purchasing Teensy or o t h e r PJRC products .
∗
∗ Permission i s hereby granted , f r e e o f charge , t o any person o b t a i n i n g a
copy
∗ o f t h i s s o f t w a r e and a s s o c i a t e d documentation f i l e s ( t h e " Software " ) , t o
deal
∗ i n t h e Software without r e s t r i c t i o n , i n c l u d i n g without l i m i t a t i o n t h e
rights
∗ t o use , copy , modify , merge , publish , d i s t r i b u t e , s u b l i c e n s e , and/or s e l l
Appendix A. Appendix 109
# i f n d e f _input_tdm_h_
# d e f i n e _input_tdm_h_
c l a s s AudioInputTDM : p u b l i c AudioStream
{
public :
AudioInputTDM ( void ) : AudioStream ( 0 , NULL) { begin ( ) ; }
v i r t u a l void update ( void ) ;
void begin ( void ) ;
protected :
AudioInputTDM ( i n t dummy) : AudioStream ( 0 , NULL) { } // t o be used only
i n s i d e Au dioInputI2Sslave ! !
s t a t i c bool u p d a t e _ r e s p o n s i b i l i t y ;
s t a t i c DMAChannel dma ;
s t a t i c void i s r ( void ) ;
private :
s t a t i c a u d i o _ b l o c k _ t ∗ block_incoming [MAX_CHANNELS] ;
};
c l a s s AudioInputTDMslave : p u b l i c AudioInputTDM
{
public :
AudioInputTDMslave ( void ) : AudioInputTDM ( 0 ) { begin ( ) ; }
void begin ( void ) ;
f r i e n d void dma_ch1_isr ( void ) ;
};
# endif
Appendix A. Appendix 110
input_tdm.cpp
/∗ Audio L i b r a r y f o r Teensy 3 . X
∗ Copyright ( c ) 2 0 1 7 , Paul S t o f f r e g e n , paul@pjrc . com
∗
∗ Development o f t h i s audio l i b r a r y was funded by PJRC .COM, LLC by s a l e s o f
∗ Teensy and Audio Adaptor boards . P l e a s e support PJRC ’ s e f f o r t s t o develop
∗ open s o u r c e s o f t w a r e by purchasing Teensy or o t h e r PJRC products .
∗
∗ Permission i s hereby granted , f r e e o f charge , t o any person o b t a i n i n g a
copy
∗ o f t h i s s o f t w a r e and a s s o c i a t e d documentation f i l e s ( t h e " Software " ) , t o
deal
∗ i n t h e Software without r e s t r i c t i o n , i n c l u d i n g without l i m i t a t i o n t h e
rights
∗ t o use , copy , modify , merge , publish , d i s t r i b u t e , s u b l i c e n s e , and/or s e l l
∗ c o p i e s o f t h e Software , and t o permit persons t o whom t h e Software i s
∗ f u r n i s h e d t o do so , s u b j e c t t o t h e f o l l o w i n g c o n d i t i o n s :
∗
∗ The above c o p y r i g h t n o t i c e , development funding n o t i c e , and t h i s
permission
∗ n o t i c e s h a l l be included i n a l l c o p i e s or s u b s t a n t i a l p o r t i o n s o f t h e
Software .
∗
∗ THE SOFTWARE I S PROVIDED "AS I S " , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
∗ IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
∗ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
∗ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
∗ LIABILITY , WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
∗ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
∗ THE SOFTWARE.
∗/
// TODO: needs o p t i m i z a t i o n . . .
s t a t i c void memcpy_tdm_rx ( u i n t 3 2 _ t ∗ dest1 , u i n t 3 2 _ t ∗ dest2 , c o n s t u i n t 3 2 _ t ∗
src )
{
u i n t 3 2 _ t i , in1 , i n 2 ;
output_tdm.h
/∗ Audio L i b r a r y f o r Teensy 3 . X
∗ Copyright ( c ) 2 0 1 7 , Paul S t o f f r e g e n , paul@pjrc . com
∗
∗ Development o f t h i s audio l i b r a r y was funded by PJRC .COM, LLC by s a l e s o f
∗ Teensy and Audio Adaptor boards . P l e a s e support PJRC ’ s e f f o r t s t o develop
∗ open s o u r c e s o f t w a r e by purchasing Teensy or o t h e r PJRC products .
∗
∗ Permission i s hereby granted , f r e e o f charge , t o any person o b t a i n i n g a
copy
∗ o f t h i s s o f t w a r e and a s s o c i a t e d documentation f i l e s ( t h e " Software " ) , t o
deal
∗ i n t h e Software without r e s t r i c t i o n , i n c l u d i n g without l i m i t a t i o n t h e
rights
∗ t o use , copy , modify , merge , publish , d i s t r i b u t e , s u b l i c e n s e , and/or s e l l
∗ c o p i e s o f t h e Software , and t o permit persons t o whom t h e Software i s
∗ f u r n i s h e d t o do so , s u b j e c t t o t h e f o l l o w i n g c o n d i t i o n s :
∗
Appendix A. Appendix 114
# i f n d e f output_tdm_h_
# d e f i n e output_tdm_h_
//# d e f i n e MAX_CHANNELS 16
# d e f i n e MAX_CHANNELS 32
# d e f i n e MAX_CHANNELS 32
c l a s s AudioOutputTDM : p u b l i c AudioStream
{
public :
AudioOutputTDM ( void ) : AudioStream (MAX_CHANNELS, inputQueueArray ) {
begin ( ) ; }
v i r t u a l void update ( void ) ;
void begin ( void ) ;
f r i e n d c l a s s AudioInputTDM ;
protected :
AudioOutputTDM ( i n t dummy) : AudioStream (MAX_CHANNELS, inputQueueArray ) {
}
s t a t i c void config_tdm ( void ) ;
s t a t i c a u d i o _ b l o c k _ t ∗ b l o c k _ i n p u t [MAX_CHANNELS] ;
s t a t i c bool u p d a t e _ r e s p o n s i b i l i t y ;
s t a t i c DMAChannel dma ;
s t a t i c void i s r ( void ) ;
private :
a u d i o _ b l o c k _ t ∗ inputQueueArray [MAX_CHANNELS] ;
};
c l a s s AudioOutputTDMslave : p u b l i c AudioOutputTDM
{
public :
AudioOutputTDMslave ( void ) : AudioOutputTDM ( 0 ) { begin ( ) ; } ;
void begin ( void ) ;
f r i e n d c l a s s AudioInputTDMslave ;
Appendix A. Appendix 115
output_tdm.cpp
/∗ Audio L i b r a r y f o r Teensy 3 . X
∗ Copyright ( c ) 2 0 1 7 , Paul S t o f f r e g e n , paul@pjrc . com
∗
∗ Development o f t h i s audio l i b r a r y was funded by PJRC .COM, LLC by s a l e s o f
∗ Teensy and Audio Adaptor boards . P l e a s e support PJRC ’ s e f f o r t s t o develop
∗ open s o u r c e s o f t w a r e by purchasing Teensy or o t h e r PJRC products .
∗
∗ Permission i s hereby granted , f r e e o f charge , t o any person o b t a i n i n g a
copy
∗ o f t h i s s o f t w a r e and a s s o c i a t e d documentation f i l e s ( t h e " Software " ) , t o
deal
∗ i n t h e Software without r e s t r i c t i o n , i n c l u d i n g without l i m i t a t i o n t h e
rights
∗ t o use , copy , modify , merge , publish , d i s t r i b u t e , s u b l i c e n s e , and/or s e l l
∗ c o p i e s o f t h e Software , and t o permit persons t o whom t h e Software i s
∗ f u r n i s h e d t o do so , s u b j e c t t o t h e f o l l o w i n g c o n d i t i o n s :
∗
∗ The above c o p y r i g h t n o t i c e , development funding n o t i c e , and t h i s
permission
∗ n o t i c e s h a l l be included i n a l l c o p i e s or s u b s t a n t i a l p o r t i o n s o f t h e
Software .
∗
∗ THE SOFTWARE I S PROVIDED "AS I S " , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
∗ IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
∗ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
∗ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
∗ LIABILITY , WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
∗ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
∗ THE SOFTWARE.
∗/
# i f d e f i n e d ( KINETISK )
a u d i o _ b l o c k _ t ∗ AudioOutputTDM : : b l o c k _ i n p u t [MAX_CHANNELS] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};
bool AudioOutputTDM : : u p d a t e _ r e s p o n s i b i l i t y = f a l s e ;
s t a t i c u i n t 3 2 _ t z e r o s [AUDIO_BLOCK_SAMPLES/ 2 ] ;
DMAMEM s t a t i c u i n t 3 2 _ t t d m _ t x _ b u f f e r [AUDIO_BLOCK_SAMPLES∗MAX_CHANNELS] ;
Appendix A. Appendix 116
dma . TCD−>SADDR = t d m _ t x _ b u f f e r ;
dma . TCD−>SOFF = 4 ;
dma . TCD−>ATTR = DMA_TCD_ATTR_SSIZE ( 2 ) | DMA_TCD_ATTR_DSIZE ( 2 ) ;
dma . TCD−>NBYTES_MLNO = 4 ;
dma . TCD−>SLAST = − s i z e o f ( t d m _ t x _ b u f f e r ) ;
dma . TCD−>DADDR = &I2S0_TDR0 ;
dma . TCD−>DOFF = 0 ;
dma . TCD−>CITER_ELINKNO = s i z e o f ( t d m _ t x _ b u f f e r ) / 4 ;
dma . TCD−>DLASTSGA = 0 ;
dma . TCD−>BITER_ELINKNO = s i z e o f ( t d m _ t x _ b u f f e r ) / 4 ;
dma . TCD−>CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR ;
dma . triggerAtHardwareEvent (DMAMUX_SOURCE_I2S0_TX) ;
u p d a t e _ r e s p o n s i b i l i t y = update_setup ( ) ;
dma . e n a b l e ( ) ;
I2S0_TCSR = I2S_TCSR_SR ;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;
dma . a t t a c h I n t e r r u p t ( i s r ) ;
}
// TODO: needs o p t i m i z a t i o n . . .
s t a t i c void memcpy_tdm_tx ( u i n t 3 2 _ t ∗ dest , c o n s t u i n t 3 2 _ t ∗ s r c 1 , c o n s t
uint32_t ∗ src2 )
{
u i n t 3 2 _ t i , in1 , in2 , out1 , out2 ;
// S e r i a l . p r i n t ( ∗ s r c 2 ,HEX) ;
f o r ( i = 0 ; i < AUDIO_BLOCK_SAMPLES/ 2 ; i ++) {
i n 1 = ∗ s r c 1 ++;
i n 2 = ∗ s r c 2 ++;
// S e r i a l . p r i n t ( " \ n " ) ;
// S e r i a l . p r i n t ( in1 ,HEX) ;
out1 = ( i n 1 << 1 6 ) | ( i n 2 & 0xFFFF ) ;
out2 = ( i n 1 & 0 xFFFF0000 ) | ( i n 2 >> 1 6 ) ;
//out1 = ( ( i n 1 & 0xFFFF ) < <16) ;
//out2 = ( ( i n 1 & 0 xFFFF0000 ) ) ;
∗ d e s t = out1 ;
Appendix A. Appendix 117
// S e r i a l . p r i n t ( " \ n " ) ;
// S e r i a l . p r i n t ( out1 ,HEX) ;
∗ ( d e s t + (MAX_CHANNELS> >1) ) = out2 ;
d e s t += MAX_CHANNELS;
}
// S e r i a l . p r i n t ( ∗ s r c ,HEX) ;
}
__disable_irq ( ) ;
f o r ( i = 0 ; i < MAX_CHANNELS; i ++) {
Appendix A. Appendix 118
prev [ i ] = b l o c k _ i n p u t [ i ] ;
b l o c k _ i n p u t [ i ] = receiveReadOnly ( i ) ;
}
__enable_irq ( ) ;
f o r ( i = 0 ; i < MAX_CHANNELS; i ++) {
i f ( prev [ i ] ) r e l e a s e ( prev [ i ] ) ;
}
}
# i f n d e f MCLK_SRC
# i f F_CPU >= 20000000
Appendix A. Appendix 119
# d e f i n e MCLK_SRC 3 // t h e PLL
# else
# d e f i n e MCLK_SRC 0 // system c l o c k
# endif
# endif
// i f e i t h e r t r a n s m i t t e r or r e c e i v e r i s enabled , do nothing
i f ( I2S0_TCSR & I2S_TCSR_TE ) r e t u r n ;
i f ( I2S0_RCSR & I2S_RCSR_RE ) r e t u r n ;
// e n a b l e MCLK output
I2S0_MCR = I2S_MCR_MICS (MCLK_SRC) | I2S_MCR_MOE ;
while ( I2S0_MCR & I2S_MCR_DUF ) ;
I2S0_MDR = I2S_MDR_FRACT ( (MCLK_MULT− 1) ) | I2S_MDR_DIVIDE ( ( MCLK_DIV− 1)
);
// c o n f i g u r e t r a n s m i t t e r
I2S0_TMR = 0 ;
I2S0_TCR1 = I2S_TCR1_TFW ( 4 ) ;
I2S0_TCR2 = I2S_TCR2_SYNC ( 0 ) | I2S_TCR2_BCP | I2S_TCR2_MSEL ( 1 )
| I2S_TCR2_BCD | I2S_TCR2_DIV ( 0 ) ;
I2S0_TCR3 = I2S_TCR3_TCE ;
I2S0_TCR4 = I2S_TCR4_FRSZ ( 7 ) | I2S_TCR4_SYWD ( 0 ) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSD ;
I2S0_TCR5 = I2S_TCR5_WNW ( 3 1 ) | I2S_TCR5_W0W ( 3 1 ) | I2S_TCR5_FBT ( 3 1 ) ;
// c o n f i g u r e r e c e i v e r ( sync ’ d t o t r a n s m i t t e r c l o c k s )
I2S0_RMR = 0 ;
I2S0_RCR1 = I2S_RCR1_RFW ( 4 ) ;
I2S0_RCR2 = I2S_RCR2_SYNC ( 1 ) | I2S_TCR2_BCP | I2S_RCR2_MSEL ( 1 )
| I2S_RCR2_BCD | I2S_RCR2_DIV ( 0 ) ;
I2S0_RCR3 = I2S_RCR3_RCE ;
I2S0_RCR4 = I2S_RCR4_FRSZ ( 7 ) | I2S_RCR4_SYWD ( 0 ) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSD ;
I2S0_RCR5 = I2S_RCR5_WNW ( 3 1 ) | I2S_RCR5_W0W ( 3 1 ) | I2S_RCR5_FBT ( 3 1 ) ;
// c o n f i g u r e pin mux f o r 3 c l o c k s i g n a l s
CORE_PIN23_CONFIG = PORT_PCR_MUX( 6 ) ; // pin 2 3 , PTC2 , I2S0_TX_FS (
LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX( 6 ) ; // pin 9 , PTC3 , I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX( 6 ) ; // pin 1 1 , PTC6 , I2S0_MCLK
}
void AudioOutputTDMslave : : config_tdm ( void )
{
S e r i a l . p r i n t ( " \n AudioOutputTDMslave : config_tdm " ) ;
SIM_SCGC6 |= SIM_SCGC6_I2S ;
Appendix A. Appendix 120
SIM_SCGC7 |= SIM_SCGC7_DMA ;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
// i f e i t h e r t r a n s m i t t e r or r e c e i v e r i s enabled , do nothing
i f ( I2S0_TCSR & I2S_TCSR_TE ) r e t u r n ;
i f ( I2S0_RCSR & I2S_RCSR_RE ) r e t u r n ;
// S e l e c t input c l o c k 0
// Configure t o input t h e b i t −c l o c k from pin , bypasses t h e MCLK
divider
I2S0_MCR = I2S_MCR_MICS ( 0 ) ;
I2S0_MDR = 0 ;
// c o n f i g u r e t r a n s m i t t e r
I2S0_TMR = 0 ;
I2S0_TCR1 = I2S_TCR1_TFW ( 8 ) ;
I2S0_TCR2 = I2S_TCR2_SYNC ( 0 ) | I2S_TCR2_BCP ;
I2S0_TCR3 = I2S_TCR3_TCE ;
I2S0_TCR4 = I2S_TCR4_FRSZ ( 1 5 ) | I2S_TCR4_SYWD ( 0 ) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP ; //|I2S_TCR4_FSD ;//FSD
I2S0_TCR5 = I2S_TCR5_WNW ( 3 1 ) | I2S_TCR5_W0W ( 3 1 ) | I2S_TCR5_FBT ( 3 1 ) ;
// c o n f i g u r e r e c e i v e r ( sync ’ d t o t r a n s m i t t e r c l o c k s )
I2S0_RMR = 0 ;
I2S0_RCR1 = I2S_RCR1_RFW ( 8 ) ;
I2S0_RCR2 = I2S_RCR2_SYNC ( 1 ) | I2S_TCR2_BCP ;
I2S0_RCR3 = I2S_RCR3_RCE ;
I2S0_RCR4 = I2S_RCR4_FRSZ ( 1 5 ) | I2S_RCR4_SYWD ( 0 ) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP| I2S_RCR4_FSD ;
I2S0_RCR5 = I2S_RCR5_WNW ( 3 1 ) | I2S_RCR5_W0W ( 3 1 ) | I2S_RCR5_FBT ( 3 1 ) ;
// c o n f i g u r e pin mux f o r 3 c l o c k s i g n a l s
CORE_PIN23_CONFIG = PORT_PCR_MUX( 6 ) ; // pin 2 3 , PTC2 , I2S0_TX_FS (
LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX( 6 ) ; // pin 9 , PTC3 , I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX( 6 ) ; // pin 1 1 , PTC6 , I2S0_MCLK
}
dma . TCD−>SADDR = t d m _ t x _ b u f f e r ;
Appendix A. Appendix 121
dma . TCD−>SOFF = 4 ;
dma . TCD−>ATTR = DMA_TCD_ATTR_SSIZE ( 2 ) | DMA_TCD_ATTR_DSIZE ( 2 ) ;
dma . TCD−>NBYTES_MLNO = 4 ;
dma . TCD−>SLAST = − s i z e o f ( t d m _ t x _ b u f f e r ) ;
dma . TCD−>DADDR = &I2S0_TDR0 ;
dma . TCD−>DOFF = 0 ;
dma . TCD−>CITER_ELINKNO = s i z e o f ( t d m _ t x _ b u f f e r ) / 4 ;
dma . TCD−>DLASTSGA = 0 ;
dma . TCD−>BITER_ELINKNO = s i z e o f ( t d m _ t x _ b u f f e r ) / 4 ;
dma . TCD−>CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR ;
dma . triggerAtHardwareEvent (DMAMUX_SOURCE_I2S0_TX) ;
u p d a t e _ r e s p o n s i b i l i t y = update_setup ( ) ;
dma . e n a b l e ( ) ;
I2S0_TCSR = I2S_TCSR_SR ;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE ;
dma . a t t a c h I n t e r r u p t ( i s r ) ;
}
# e n d i f // KINETISK
effect_freeverb.h
/∗ F r eeverb f o r t e e n s y
∗
∗ Copyright ( c ) 2 0 1 7 , Yasmeen S u l t a n a
∗
∗
∗ Permission i s hereby granted , f r e e o f charge , t o any person o b t a i n i n g a
copy
∗ o f t h i s s o f t w a r e and a s s o c i a t e d documentation f i l e s ( t h e " Software " ) , t o
deal
∗ i n t h e Software without r e s t r i c t i o n , i n c l u d i n g without l i m i t a t i o n t h e
rights
∗ t o use , copy , modify , merge , publish , d i s t r i b u t e , s u b l i c e n s e , and/or s e l l
∗ c o p i e s o f t h e Software , and t o permit persons t o whom t h e Software i s
∗ f u r n i s h e d t o do so , s u b j e c t t o t h e f o l l o w i n g c o n d i t i o n s :
∗
∗ The above c o p y r i g h t n o t i c e , development funding n o t i c e , and t h i s permission
∗ n o t i c e s h a l l be included i n a l l c o p i e s or s u b s t a n t i a l p o r t i o n s o f t h e
Software .
∗
∗ THE SOFTWARE I S PROVIDED "AS I S " , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
∗ IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
∗ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
∗ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
∗ LIABILITY , WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
∗ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
∗ THE SOFTWARE.
∗/
Appendix A. Appendix 122
# ifndef effect_freeverb_
# define effect_freeverb_
/∗ A l l pass f i l t e r s ∗/
# d e f i n e APF_COUNT 4
# d e f i n e LBCF_COUNT 8
# d e f i n e RIGHT_STERO_OFFSET 23
c l a s s A u d i o E f f e c t F r e e v e r b : p u b l i c AudioStream
{
public :
A u d i o E f f e c t F r e e v e r b ( void ) : AudioStream ( 1 , inputQueueArray )
{
/∗ c o n s t r u c t o r ∗/
init_apf (0.5 f ,0) ;
init_lbcf (0.84 f ,0.2 f ,0) ;
}
A u d i o E f f e c t F r e e v e r b ( bool r i g h t C h a n n e l ) : AudioStream ( 1 ,
inputQueueArray )
{
i f ( r i g h t C h a n n e l== t r u e )
{
i n i t _ a p f ( 0 . 5 f , RIGHT_STERO_OFFSET ) ;
i n i t _ l b c f ( 0 . 8 4 f , 0 . 2 f , RIGHT_STERO_OFFSET ) ;
}
else
{
init_apf ( 0 . 5 f , 0) ;
i n i t _ l b c f (0.84 f , 0.2 f , 0) ;
}
}
Appendix A. Appendix 123
private :
struct allpass_filter
{
int32_t gain ;
int32_t gain1 ;
int32_t ∗ pbuffer ;
uint32_t buf_len ;
u i n t 3 2 _ t delay ;
uint32_t bufferIndex ;
};
s t r u c t lowpass_comb_filter
{
int32_t damp1 ; /∗d∗/
int32_t damp2 ; /∗1−d∗/
int32_t feedback ; /∗ f ∗/
int32_t s t a t e _ z 1 ; /∗∗/
int32_t ∗ pbuffer ;
uint32_t buf_len ;
u i n t 3 2 _ t delay ;
uint32_t bufferIndex ;
};
a u d i o _ b l o c k _ t ∗ inputQueueArray [ 1 ] ;
i n t 3 2 _ t q31_buf [AUDIO_BLOCK_SAMPLES ] ;
i n t 3 2 _ t sum_buf [AUDIO_BLOCK_SAMPLES ] ;
i n t 3 2 _ t aux_buf [AUDIO_BLOCK_SAMPLES ] ;
};
# endif
effect_freeverb.cpp
/∗
∗ Copyright ( c ) 2017 Yasmeen S u l t a n a
∗
∗ Permission i s hereby granted , f r e e o f charge , t o any person o b t a i n i n g a
copy
∗ o f t h i s s o f t w a r e and a s s o c i a t e d documentation f i l e s ( t h e " Software " ) , t o
deal
∗ i n t h e Software without r e s t r i c t i o n , i n c l u d i n g without l i m i t a t i o n t h e
rights
∗ t o use , copy , modify , merge , publish , d i s t r i b u t e , s u b l i c e n s e , and/or s e l l
∗ c o p i e s o f t h e Software , and t o permit persons t o whom t h e Software i s
∗ f u r n i s h e d t o do so , s u b j e c t t o t h e f o l l o w i n g c o n d i t i o n s :
∗
∗ The above c o p y r i g h t n o t i c e and t h i s permission n o t i c e s h a l l be included i n
all
∗ c o p i e s or s u b s t a n t i a l p o r t i o n s o f t h e Software .
∗
∗ THE SOFTWARE I S PROVIDED "AS I S " , WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
∗ IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
∗ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE
∗ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
∗ LIABILITY , WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
∗ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE
∗ SOFTWARE.
∗/
#if 1
void
A u d i o E f f e c t F r e e v e r b : : p r o c e s s _ a p f ( s t r u c t a l l p a s s _ f i l t e r ∗ apf , i n t 3 2 _ t ∗ in_buf ,
i n t 3 2 _ t ∗ out_buf )
{
i n t 3 2 _ t bufout ;
i n t 3 2 _ t input ;
i n t 3 2 _ t z1 ,w;
int32_t n;
i n t 3 2 _ t output ;
//bufout = b u f f e r [ bufidx ] ;
// b u f f e r [ bufidx ] = input + ( bufout ∗ feedback ) ;
//output = − b u f f e r [ bufidx ] ∗ feedback + bufout ;
#if 0
void
A u d i o E f f e c t F r e e v e r b : : p r o c e s s _ a p f ( s t r u c t a l l p a s s _ f i l t e r ∗ apf , i n t 3 2 _ t ∗ in_buf ,
i n t 3 2 _ t ∗ out_buf )
{
i n t 3 2 _ t bufout ;
i n t 3 2 _ t input ;
i n t 3 2 _ t z1 ,w;
int32_t n;
i n t 3 2 _ t output ;
i n t 3 2 _ t bufGain ;
//bufout = b u f f e r [ bufidx ] ;
// b u f f e r [ bufidx ] = input + ( bufout ∗ feedback ) ;
//output = − b u f f e r [ bufidx ] ∗ feedback + bufout ;
Appendix A. Appendix 126
}
}
void A u d i o E f f e c t F r e e v e r b : : i n i t _ a p f ( f l o a t gain , i n t 3 2 _ t o f f s e t )
{
uint8_t n = 0;
apf [ n ] . b u f f e r I n d e x = 0 ;
}
apf [ 0 ] . p b u f f e r = &apf1_buf [ 0 ] ;
apf [ 0 ] . delay = APF1_DELAY_LENGTH + o f f s e t ;
apf [ 0 ] . b u f _ l e n = APF1_DELAY_LENGTH + o f f s e t ;
apf [ 1 ] . p b u f f e r = &apf2_buf [ 0 ] ;
apf [ 1 ] . delay = APF2_DELAY_LENGTH + o f f s e t ;
apf [ 1 ] . b u f _ l e n = APF2_DELAY_LENGTH + o f f s e t ;
apf [ 2 ] . p b u f f e r = &apf3_buf [ 0 ] ;
apf [ 2 ] . delay = APF3_DELAY_LENGTH + o f f s e t ;
apf [ 2 ] . b u f _ l e n = APF3_DELAY_LENGTH + o f f s e t ;
apf [ 3 ] . p b u f f e r = &apf4_buf [ 0 ] ;
apf [ 3 ] . delay = APF4_DELAY_LENGTH + o f f s e t ;
apf [ 3 ] . b u f _ l e n = APF4_DELAY_LENGTH + o f f s e t ;
l b c f [ 0 ] . p b u f f e r = &l b c f 1 _ b u f [ 0 ] ;
l b c f [ 0 ] . delay = LBCF1_DELAY_LENGTH+ o f f s e t ;
l b c f [ 0 ] . b u f _ l e n = LBCF1_DELAY_LENGTH+ o f f s e t ;
l b c f [ 1 ] . p b u f f e r = &l b c f 2 _ b u f [ 0 ] ;
l b c f [ 1 ] . delay = LBCF2_DELAY_LENGTH + o f f s e t ;
l b c f [ 1 ] . b u f _ l e n = LBCF2_DELAY_LENGTH + o f f s e t ;
l b c f [ 2 ] . p b u f f e r = &l b c f 3 _ b u f [ 0 ] ;
l b c f [ 2 ] . delay = LBCF3_DELAY_LENGTH + o f f s e t ;
l b c f [ 2 ] . b u f _ l e n = LBCF3_DELAY_LENGTH + o f f s e t ;
l b c f [ 3 ] . p b u f f e r = &l b c f 4 _ b u f [ 0 ] ;
l b c f [ 3 ] . delay = LBCF4_DELAY_LENGTH + o f f s e t ;
l b c f [ 3 ] . b u f _ l e n = LBCF4_DELAY_LENGTH + o f f s e t ;
l b c f [ 4 ] . p b u f f e r = &l b c f 5 _ b u f [ 0 ] ;
l b c f [ 4 ] . delay = LBCF5_DELAY_LENGTH + o f f s e t ;
l b c f [ 4 ] . b u f _ l e n = LBCF5_DELAY_LENGTH + o f f s e t ;
l b c f [ 5 ] . p b u f f e r = &l b c f 6 _ b u f [ 0 ] ;
l b c f [ 5 ] . delay = LBCF6_DELAY_LENGTH + o f f s e t ;
l b c f [ 5 ] . b u f _ l e n = LBCF6_DELAY_LENGTH + o f f s e t ;
l b c f [ 6 ] . p b u f f e r = &l b c f 7 _ b u f [ 0 ] ;
l b c f [ 6 ] . delay = LBCF7_DELAY_LENGTH + o f f s e t ;
l b c f [ 6 ] . b u f _ l e n = LBCF7_DELAY_LENGTH + o f f s e t ;
l b c f [ 7 ] . p b u f f e r = &l b c f 8 _ b u f [ 0 ] ;
l b c f [ 7 ] . delay = LBCF8_DELAY_LENGTH + o f f s e t ;
l b c f [ 7 ] . b u f _ l e n = LBCF8_DELAY_LENGTH + o f f s e t ;
}
void
A u d i o E f f e c t F r e e v e r b : : update ( void )
{
audio_block_t ∗ block ;
if ( ! ( block = receiveWritable ( ) ) )
return ;
i f ( ! block −>data )
Appendix A. Appendix 129
return ;
t r a n s m i t ( block , 0 ) ;
r e l e a s e ( block ) ;
}
ctag_face24_reverb.ino
# i n c l u d e <Audio . h>
# i n c l u d e <Wire . h>
# i n c l u d e <SPI . h>
/∗
AD1938 pin s Teensy gpio pi ns
Chip s e l e c t − 7
MOSI − 11
MISO −12
SCK −14
RESET −17
Appendix A. Appendix 130
DAC/ADCBLK −9
DAC/ADCLRCLK −23
DSDATA1 TX −22
ASDARA1 RX −13
∗/
/∗
The d a i s y chain c o n n e c t i o n o f two ad1938 and Teensy a r e as f o l l o w s
AD1938 ( s l a v e ) −>AD1938 ( master ) −>Teensy ( s l a v e )
∗/
int c l a t c h _ s l a v e = 6 ; //ad1938 s l a v e s p i l a t c h
int c l a tc h =7; //ad1938 master s p i l a t c h
int cout = 1 2 ; // s p i miso
int cin =11; // s p i mosi
int cclk =14; // s p i c l o c k
int r e s e t _ p i n _ s l a v e = 1 6 ; //ad1938 s l a v e r e s e t pin
int reset_pin =17; //ad1938 master r e s e t pin
AudioControlAD1938 ad1938master ;
AudioControlAD1938 ad1938slave ;
AudioMixer4 input_gain ;
AudioMixer4 left_mix ;
AudioMixer4 right_mix ;
/∗ d e f a u l t v a l u e s 0 . 5 f , 0 . 8 4 f , 0 . 2 ∗/
AudioEffectFreeverb reverbL ( 0 . 5 f , 0 . 8 4 f , 0 . 2 f , f a l s e ) ; /∗ a l l p a s s gain ,
feedback , damping , s t e r e o pad∗/
AudioEffectFreeverb reverbR ( 0 . 5 f , 0 . 8 4 f , 0 . 2 f , t r u e ) ; /∗adding s t e r e o
padding f o r r i g h t channel ∗/
#if 1
//only f o r s l a v e ++
//AudioInputTDM i2s_in ;
//AudioOutputTDM i2s_out ;
//only f o r s l a v e −−
AudioSynthWaveformSine sine1 ;
/∗ADC0 t o DAC0∗/
//AudioConnection pc0 ( i 2 s _ i n , 0 , i2s_out , 0) ; // ADC1 L −> DAC1 L
//AudioConnection pc2 ( i 2 s _ i n , 2 , i2s_out , 2) ; // ADC1 R −> DAC1 R
/∗ f r e e v e r b ∗/
/∗mix l e f t and r i g h t c h a n n e l s ∗/
AudioConnection patchCord1 ( i 2 s _ i n , 0 , input_gain , 0 ) ;
AudioConnection patchCord2 ( i 2 s _ i n , 2 , input_gain , 1 ) ;
Appendix A. Appendix 131
/∗ g i v e t h e combined s i g n a l t o f r e e v e r b c l a s s ∗/
AudioConnection patchCord3 ( input_gain , reverbL ) ;
AudioConnection patchCord4 ( input_gain , reverbR ) ;
/∗Mix t h e l e f t channel ∗/
AudioConnection patchCord5 ( reverbL , 0 , l e f t _ m i x , 0 ) ;
AudioConnection patchCord6 ( reverbR , 0 , l e f t _ m i x , 1 ) ;
AudioConnection patchCord7 ( i 2 s _ i n , 0 , l e f t _ m i x , 2 ) ;
/∗Mix t h e r i g h t channel ∗/
AudioConnection patchCord8 ( reverbR , 0 , right_mix , 0 ) ;
AudioConnection patchCord9 ( reverbL , 0 , right_mix , 1 ) ;
AudioConnection patchCord10 ( i 2 s _ i n , 1 , right_mix , 2 ) ;
AudioConnection patchCord11 ( l e f t _ m i x , 0 , i 2 s _ o u t , 0 ) ;
AudioConnection patchCord12 ( right_mix , 0 , i 2 s _ o u t , 2 ) ;
/∗ADC1 t o DAC1∗/
AudioConnection pc4 ( i 2 s _ i n , 4 , i 2 s _ o u t , 4 ) ; // ADC2 L −> DAC2 L
AudioConnection pc6 ( i 2 s _ i n , 6 , i 2 s _ o u t , 6 ) ; // ADC2 R −> DAC2 R
/∗ADC2 t o DAC2∗/
AudioConnection pc8 ( i 2 s _ i n , 8 , i 2 s _ o u t , 1 2 ) ; // ADC3 L −> DAC3 L
AudioConnection pc10 ( i 2 s _ i n , 1 0 , i 2 s _ o u t , 1 4 ) ; // ADC3 R −> DAC3 R
/∗ADC3 t o DAC3∗/
AudioConnection pc12 ( i 2 s _ i n , 1 2 , i 2 s _ o u t , 8 ) ; // ADC4 L −> DAC4 L
AudioConnection pc14 ( i 2 s _ i n , 1 4 , i 2 s _ o u t , 1 0 ) ; // ADC4 R −> DAC4 R
# i f 1//TMD16
/∗ s i n e t o DAC4∗/
AudioConnection pc16 ( i 2 s _ i n , 0 , i 2 s _ o u t , 1 6 ) ; // ADC1 L −> DAC5 L
AudioConnection pc18 ( i 2 s _ i n , 2 , i 2 s _ o u t , 1 8 ) ; // ADC1 R −> DAC5 R
/∗ s i n e t o DAC5∗/
AudioConnection pc20 ( i 2 s _ i n , 4 , i 2 s _ o u t , 2 0 ) ; // ADC2 L −> DAC6 L
AudioConnection pc22 ( i 2 s _ i n , 6 , i 2 s _ o u t , 2 2 ) ; // ADC2 R −> DAC6 R
/∗ s i n e t o DAC6∗/
AudioConnection pc24 ( i 2 s _ i n , 0 , i 2 s _ o u t , 2 4 ) ; // s i n e −> DAC7 L
AudioConnection pc26 ( i 2 s _ i n , 2 , i 2 s _ o u t , 2 6 ) ; // s i n e −> DAC7 R
/∗ s i n e t o DAC 7∗/
AudioConnection pc28 ( s i n e 1 , 0 , i 2 s _ o u t , 2 8 ) ; // s i n e −> DAC8 L
AudioConnection pc30 ( s i n e 1 , 0 , i 2 s _ o u t , 3 0 ) ; // s i n e −> DAC8 R
# endif
# endif
void setup ( ) {
f l o a t wet , wet1 , wet2 , dry , dry1 ;
c o n s t f l o a t scaleWet = 3 ;
const f l o a t scaleDry = 2 ;
Appendix A. Appendix 132
f l o a t e f f e c t M i x = 0 . 5 ; // only wet
f l o a t width = 0 . 5 ; // complete s e p e r a t i o n ( 1 no e f f e c t from t h e o t h e r channel
)
#if 1
wet1 = scaleWet ∗ e f f e c t M i x ;
dry1 = s c a l e D r y ∗ (1.0 − e f f e c t M i x ) ;
i n p u t _ g a i n . gain ( 0 , 0 . 5 ) ;
i n p u t _ g a i n . gain ( 1 , 0 . 5 ) ;
/∗
wet1 = 1 . 0 ; wet2 = 0 ; dry = 1 . 0 ;
∗/
/∗ l e f t channel matrix ∗/
l e f t _ m i x . gain ( 0 , wet1 ) ;
l e f t _ m i x . gain ( 1 , wet2 ) ;
l e f t _ m i x . gain ( 2 , dry ) ;
#if 1
a d 1 9 3 8 s l a v e . s p i I n i t ( c l a t c h _ s l a v e , r e s e t _ p i n _ s l a v e , cout , cin , c c l k ) ;
delay ( 2 0 0 ) ;
//a d 1 9 3 8 s l a v e . c o n f i g ( FS_48000 , BITS_24 , I2S_TDM_8CH , AD1938_I2S_SLAVE ) ;
a d 1 9 3 8 s l a v e . c o n f i g ( FS_48000 , BITS_16 , I2S_TDM_16CH , AD1938_I2S_SLAVE ) ;
a d 1 9 3 8 s l a v e . volume ( 1 ) ;
delay ( 2 0 0 ) ;
# endif
#if 1
/∗ c o n f i g u r e AD1938 ( s l a v e ) wit ∗/
ad1938master . s p i I n i t ( c l a t c h , r e s e t _ p i n , cout , cin , c c l k ) ;
delay ( 2 0 0 ) ;
Appendix A. Appendix 133
# endif
AudioMemory ( 2 5 6 ) ;
AudioInterrupts ( ) ;
AudioProcessorUsageMaxReset ( ) ;
AudioMemoryUsageMaxReset ( ) ;
reverbL . processorUsageMaxReset ( ) ;
}
void loop ( ) {
// put your main code here , t o run r e p e a t e d l y :
S e r i a l . p r i n t ( " \n " ) ;
S e r i a l . p r i n t ( "CPU : " ) ;
S e r i a l . print ( " freeverb l e f t =" ) ;
S e r i a l . p r i n t ( reverbL . processorUsage ( ) ) ;
Serial . print ( " , " ) ;
S e r i a l . p r i n t ( reverbL . processorUsageMax ( ) ) ;
Serial . print ( " " ) ;
S e r i a l . p r i n t ( " Freeverb r i g h t = " ) ;
S e r i a l . p r i n t ( reverbR . processorUsage ( ) ) ;
Serial . print ( " , " ) ;
S e r i a l . p r i n t ( reverbR . processorUsageMax ( ) ) ;
Serial . print ( " " ) ;
S e r i a l . print ( " I2S =" ) ;
S e r i a l . p r i n t ( i 2 s _ i n . processorUsage ( ) ) ;
Serial . print ( " , " ) ;
S e r i a l . p r i n t ( i 2 s _ i n . processorUsageMax ( ) ) ;
Serial . print ( " " ) ;
S e r i a l . print ( " a l l =" ) ;
S e r i a l . p r i n t ( AudioProcessorUsage ( ) ) ;
Serial . print ( " , " ) ;
S e r i a l . p r i n t ( AudioProcessorUsageMax ( ) ) ;
Serial . print ( " ");
S e r i a l . p r i n t ( "Memory : " ) ;
S e r i a l . p r i n t ( AudioMemoryUsage ( ) ) ;
Serial . print ( " , " ) ;
S e r i a l . p r i n t ( AudioMemoryUsageMax ( ) ) ;
Serial . print ( " ");
Serial . println () ;
delay ( 1 0 0 0 ) ;
}