upgrade Audio_SdFat & teensy-aruidno latest..

This commit is contained in:
Dooho Yi 2019-12-30 11:58:26 +09:00
parent 2fdc5dfe09
commit bd39410b91
80 changed files with 5009 additions and 362 deletions

View file

@ -1 +0,0 @@
*.swp

View file

@ -1,46 +0,0 @@
{
"name": "Audio",
"frameworks": [
"arduino"
],
"platforms": [
"teensy"
],
"keywords": [
"sound",
"audio",
"fft",
"filter",
"effect"
],
"description": "Teensy Audio Library",
"version": "1.3",
"export": {
"exclude": [
"gui",
"extras"
]
},
"authors": [
{
"email": null,
"url": null,
"maintainer": true,
"name": "Paul Stoffregen"
}
],
"repository": {
"type": "git",
"url": "https://github.com/PaulStoffregen/Audio"
},
"dependencies": {
"name": "SerialFlash",
"frameworks": "arduino"
},
"examples": [
"examples/*/*.ino",
"examples/*/*/*.ino"
],
"homepage": "http://www.pjrc.com/teensy/td_libs_Audio.html",
"id": 70
}

View file

@ -91,18 +91,26 @@
#include "input_adc.h"
#include "input_adcs.h"
#include "input_i2s.h"
#include "input_i2s2.h"
#include "input_i2s_quad.h"
#include "input_tdm.h"
#include "input_tdm2.h"
#include "input_pdm.h"
#include "mixer.h"
#include "output_dac.h"
#include "output_dacs.h"
#include "output_i2s.h"
#include "output_i2s2.h"
#include "output_i2s_quad.h"
#include "output_mqs.h"
#include "output_pwm.h"
#include "output_spdif.h"
#include "output_spdif2.h"
#include "output_spdif3.h"
#include "output_pt8211.h"
#include "output_pt8211_2.h"
#include "output_tdm.h"
#include "output_tdm2.h"
#include "output_adat.h"
#include "play_memory.h"
#include "play_queue.h"

View file

@ -62,7 +62,7 @@ void AudioAnalyzeFFT1024::update(void)
block = receiveReadOnly();
if (!block) return;
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
switch (state) {
case 0:
blocklist[0] = block;

View file

@ -36,7 +36,7 @@ void AudioAnalyzeRMS::update(void)
count++;
return;
}
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
uint32_t *p = (uint32_t *)(block->data);
uint32_t *end = p + AUDIO_BLOCK_SAMPLES/2;
int64_t sum = accum;

View file

@ -28,7 +28,7 @@
#include "analyze_tonedetect.h"
#include "utility/dspinst.h"
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
static inline int32_t multiply_32x32_rshift30(int32_t a, int32_t b) __attribute__((always_inline));
static inline int32_t multiply_32x32_rshift30(int32_t a, int32_t b)

View file

@ -78,6 +78,11 @@
// then un-mute the DACs.
// 9. Normal operation begins.
// Some people have found their CS42448 goes into a strange mode where VQ is 1.25V
// instead of the normal 2.5V. Apparently there is a workaround for this problem
// which involves writing to an undocumented bit. Details here:
// https://forum.pjrc.com/threads/41371?p=215881&viewfull=1#post215881
static const uint8_t default_config[] = {
0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max
0x76, // CS42448_Interface_Formats = TDM mode

View file

@ -519,7 +519,8 @@ bool AudioControlSGTL5000::enable(void)
//unsigned int n = read(CHIP_ID);
//Serial.println(n, HEX);
write(CHIP_ANA_POWER, 0x4060); // VDDD is externally driven with 1.8V
int r = write(CHIP_ANA_POWER, 0x4060); // VDDD is externally driven with 1.8V
if (!r) return false;
write(CHIP_LINREG_CTRL, 0x006C); // VDDA & VDDIO both over 3.1V
write(CHIP_REF_CTRL, 0x01F2); // VAG=1.575, normal ramp, +12.5% bias current
write(CHIP_LINE_OUT_CTRL, 0x0F22); // LO_VAGCNTRL=1.65V, OUT_CURRENT=0.54mA

View file

@ -47,8 +47,11 @@ bool AudioControlWM8731::enable(void)
{
Wire.begin();
delay(5);
//write(WM8731_REG_RESET, 0);
#if 1
if (!write(WM8731_REG_RESET, 0)) {
return false; // no WM8731 chip responding
}
#endif
write(WM8731_REG_INTERFACE, 0x02); // I2S, 16 bit, MCLK slave
write(WM8731_REG_SAMPLING, 0x20); // 256*Fs, 44.1 kHz, MCLK/1
@ -74,14 +77,31 @@ bool AudioControlWM8731::enable(void)
return true;
}
// WM8731 has flaky I2C communication, especially when MCLK has ringing,
// overshoot or other high-speed signal quality issues. Chips like
// Teensy 3.6 with very high bandwidth I/O pins should be used with
// caution. A resistor or low-pass circuit may be needed.
// https://forum.pjrc.com/threads/55334?p=201494&viewfull=1#post201494
bool AudioControlWM8731::write(unsigned int reg, unsigned int val)
{
Wire.beginTransmission(WM8731_I2C_ADDR);
Wire.write((reg << 1) | ((val >> 8) & 1));
Wire.write(val & 0xFF);
Wire.endTransmission();
return true;
int attempt=0;
while (1) {
attempt++;
Wire.beginTransmission(WM8731_I2C_ADDR);
Wire.write((reg << 1) | ((val >> 8) & 1));
Wire.write(val & 0xFF);
int status = Wire.endTransmission();
if (status == 0) {
//Serial.printf("WM8731 write ok, %d tries\n", attempt);
return true;
}
if (attempt >= 12) {
//Serial.printf("WM8731 write failed, %d tries\n", attempt);
return false;
}
delayMicroseconds(80);
}
}
bool AudioControlWM8731::volumeInteger(unsigned int n)

View file

@ -0,0 +1,63 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 <stdint.h>
const __attribute__((aligned(32)))
uint16_t spdif_bmclookup[256] = { //biphase mark encoded values (least significant bit first)
0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
};

View file

@ -116,7 +116,7 @@ static const audio_block_t zeroblock = {
void AudioEffectFreeverb::update()
{
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
const audio_block_t *block;
audio_block_t *outblock;
int i;
@ -294,7 +294,7 @@ AudioEffectFreeverbStereo::AudioEffectFreeverbStereo() : AudioStream(1, inputQue
void AudioEffectFreeverbStereo::update()
{
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
const audio_block_t *block;
audio_block_t *outblockL;
audio_block_t *outblockR;

View file

@ -42,7 +42,7 @@ void AudioEffectMidSide::update(void)
if (blockb) release(blockb); // of the blocks is NULL then it's trouble anyway
return;
}
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
pa = (uint32_t *)(blocka->data);
pb = (uint32_t *)(blockb->data);
end = pa + AUDIO_BLOCK_SAMPLES/2;

View file

@ -29,7 +29,7 @@
void AudioEffectMultiply::update(void)
{
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
audio_block_t *blocka, *blockb;
uint32_t *pa, *pb, *end;
uint32_t a12, a34; //, a56, a78;

View file

@ -137,14 +137,14 @@ AudioEffectReverb::init_comb_filters(void)
void
AudioEffectReverb::clear_buffers(void)
{
memset(apf1_buf, 0, APF1_BUF_LEN);
memset(apf2_buf, 0, APF1_BUF_LEN);
memset(apf3_buf, 0, APF1_BUF_LEN);
memset(apf1_buf, 0, sizeof(apf1_buf));
memset(apf2_buf, 0, sizeof(apf2_buf));
memset(apf3_buf, 0, sizeof(apf3_buf));
memset(lpf1_buf, 0, LPF1_BUF_LEN);
memset(lpf2_buf, 0, LPF2_BUF_LEN);
memset(lpf3_buf, 0, LPF3_BUF_LEN);
memset(lpf4_buf, 0, LPF4_BUF_LEN);
memset(lpf1_buf, 0, sizeof(lpf1_buf));
memset(lpf2_buf, 0, sizeof(lpf2_buf));
memset(lpf3_buf, 0, sizeof(lpf3_buf));
memset(lpf4_buf, 0, sizeof(lpf4_buf));
}
void

View file

@ -23,6 +23,10 @@
// 3.3V +3.3V
// GND GND
//
// For connection using I2S master mode (WM8731 in slave mode, with MCLK):
// https://forum.pjrc.com/threads/53854?p=198733&viewfull=1#post198733
// https://forum.pjrc.com/threads/55334?p=201494&viewfull=1#post201494
//
// This example code is in the public domain.
#include <Audio.h>

View file

@ -1,8 +1,9 @@
// Audio data converted from WAV file by wav2sketch
#include "AudioSampleCashregister.h"
#include <Arduino.h>
// Converted from cashregister.wav, using 22050 Hz, u-law encoding
PROGMEM
const unsigned int AudioSampleCashregister[5809] = {
0x02005AB4,0x82060707,0x03010301,0x08038287,0x81820200,0x09120407,0x15091108,0x02080611,
0x0D050D11,0x8008150C,0x93810480,0x000D8890,0x0A060406,0x06000681,0x80828702,0x89928405,

View file

@ -1,8 +1,9 @@
// Audio data converted from WAV file by wav2sketch
#include "AudioSampleGong.h"
#include <Arduino.h>
// Converted from gong.wav, using 11025 Hz, u-law encoding
PROGMEM
const unsigned int AudioSampleGong[27633] = {
0x0301AFA4,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00008000,0x00000000,0x00000000,0x80800000,

View file

@ -1,8 +1,9 @@
// Audio data converted from WAV file by wav2sketch
#include "AudioSampleHihat.h"
#include <Arduino.h>
// Converted from hihat.wav, using 44100 Hz, u-law encoding
PROGMEM
const unsigned int AudioSampleHihat[5953] = {
0x01005CB1,0x77766877,0xEAEC2195,0xE0737099,0x82807B70,0x88909012,0x8B822303,0x8C04180B,
0x090B9207,0x878EA413,0x1EB09F15,0x0B9B2122,0xA09E0094,0x51B2B980,0xD1485CCA,0xDACCBF48,

View file

@ -1,8 +1,9 @@
// Audio data converted from WAV file by wav2sketch
#include "AudioSampleKick.h"
#include <Arduino.h>
// Converted from kick.wav, using 22050 Hz, 16 bit PCM encoding
PROGMEM
const unsigned int AudioSampleKick[2561] = {
0x820013EC,0xFFDC0027,0xFF710095,0x038DFF4C,0xFBA10105,0x0037FB6E,0x09D2011A,0x007504CA,
0x024BF6BF,0xF77B0AFF,0xFB72F723,0xEF3DEBAD,0x0F7009D6,0x09DD1736,0xF26EFA3E,0x0D7002FB,

View file

@ -1,8 +1,9 @@
// Audio data converted from WAV file by wav2sketch
#include "AudioSampleSnare.h"
#include <Arduino.h>
// Converted from snare.wav, using 22050 Hz, u-law encoding
PROGMEM
const unsigned int AudioSampleSnare[2817] = {
0x02002BD3,0x65636656,0x6B6A6B67,0x7071706F,0x43637171,0x29ABBA23,0x3137474C,0x3A4A544D,
0x30C1542C,0xE14F6360,0xEDDCE2E6,0xEEF1F4F4,0xEEEDEAEA,0x3745A2E2,0xE7E6DAC8,0xC7C3C3DA,

View file

@ -1,8 +1,9 @@
// Audio data converted from WAV file by wav2sketch
#include "AudioSampleTomtom.h"
#include <Arduino.h>
// Converted from tomtom.wav, using 11025 Hz, 16 bit PCM encoding
PROGMEM
const unsigned int AudioSampleTomtom[3489] = {
0x83001B3C,0xFC0EFF8B,0xFB40FD97,0x045BFB30,0xEABCEAD6,0x04C2F8B3,0x002B0733,0xFB1C0879,
0xE9CCED7A,0xEC2AEC4D,0xF7D8EF09,0xF6EC0EE6,0x15A4F3A6,0xFAD81B95,0xF591F001,0x0A3FFF14,

View file

@ -1,10 +1,6 @@
// Playtune bytestream for file "william_tell_overture.mid" created by MIDITONES V1.6 on Sat Sep 6 16:56:56 2014
// command line: ./miditones -t16 -v william_tell_overture
#ifdef __AVR__
#include <avr/pgmspace.h>
#else
#define PROGMEM
#endif
#include <Arduino.h>
const unsigned char PROGMEM score [] = {
0x90,72,127, 0x91,72,107, 0x92,72,127, 0x93,72,127, 0x94,72,127, 1,169, 0x80, 0x81, 0x82, 0x83,
0x84, 0,183, 0x90,72,127, 0x91,72,107, 0x92,72,127, 0x93,72,127, 0x94,72,127, 0,41, 0x80,

View file

@ -1,5 +1,6 @@
#include "Flute_100kbyte_samples.h"
PROGMEM
static const uint32_t sample_0_Flute_100kbyte_FluteD4[7936] = {
0xfbfffc4b,0xfba4fbac,0xfbbffbb2,0xfc00fbdc,0xfc3afc16,0xfcb8fc57,0xfcf9fcf3,0xfc87fcad,
0xfcabfc77,0xfd8cfd0c,0xfe7dfe0c,0xff24fee0,0xffc4ff61,0x006a002d,0x002d004c,0xffe90000,
@ -988,6 +989,7 @@ static const uint32_t sample_0_Flute_100kbyte_FluteD4[7936] = {
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
static const uint32_t sample_1_Flute_100kbyte_FluteE5[9472] = {
0x03f00410,0x03f003c0,0x04500410,0x04500460,0x04200410,0x03800400,0x02f00320,0x01900230,
0x005000c0,0xff30ffb0,0xff70ff40,0xfe10fe90,0xfd70fde0,0xfc70fcc0,0xfc30fc00,0xfc40fca0,
@ -2169,6 +2171,7 @@ static const uint32_t sample_1_Flute_100kbyte_FluteE5[9472] = {
0x00000000,0x00000000,
};
PROGMEM
static const uint32_t sample_2_Flute_100kbyte_FluteE6[7936] = {
0x01b003e0,0x02e00250,0x01300150,0x003000f0,0x01400180,0xffc0fed0,0xff600100,0x0080ffc0,
0xffd0feb0,0xff500130,0x0070feb0,0xfed00060,0x00d0ffc0,0xff10ff50,0xff500030,0x0080ff60,
@ -3241,4 +3244,3 @@ static const AudioSynthWavetable::sample_data Flute_100kbyte_samples[3] = {
static const uint8_t Flute_100kbyte_ranges[] = {68, 83, 127, };
const AudioSynthWavetable::instrument_data Flute_100kbyte = {3, Flute_100kbyte_ranges, Flute_100kbyte_samples };

View file

@ -1,5 +1,6 @@
#include "MutedTrumpet_samples.h"
PROGMEM
static const uint32_t sample_0_MutedTrumpet_HRMMUTED4[8192] = {
0xffdaffd4,0xffdeffdd,0xffe4ffe1,0xffe2ffe5,0xffd0ffda,0xffbfffc7,0xffb8ffb9,0xffb8ffb9,
0xffb1ffb3,0xffaeffb1,0xffabffaa,0xffb1ffaf,0xffacffb0,0xffa2ffa7,0xffa3ffa0,0xffa9ffa7,
@ -1018,6 +1019,7 @@ static const uint32_t sample_0_MutedTrumpet_HRMMUTED4[8192] = {
0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
static const uint32_t sample_1_MutedTrumpet_HRMMUTEC5[6912] = {
0x00000000,0xfffeffff,0x0000ffff,0xffff0000,0xfffffffe,0xffffffff,0xfffdfffe,0xfffdfffd,
0xfffbfffc,0xfff9fff9,0xfffbfffa,0xfffbfffb,0xfffafffb,0xfffafff9,0xfffbfffb,0xfff8fff9,
@ -1875,6 +1877,7 @@ static const uint32_t sample_1_MutedTrumpet_HRMMUTEC5[6912] = {
0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
static const uint32_t sample_2_MutedTrumpet_HRMMUTEC6[4608] = {
0xff89ff87,0xff85ff8a,0xff7cff7f,0xff7bff7b,0xff79ff7a,0xff74ff77,0xff73ff72,0xff7cff78,
0xff82ff80,0xff85ff83,0xff8bff88,0xff8eff8d,0xff90ff90,0xff8cff8f,0xff87ff89,0xff7eff83,
@ -2531,4 +2534,3 @@ static const AudioSynthWavetable::sample_data MutedTrumpet_samples[3] = {
static const uint8_t MutedTrumpet_ranges[] = {71, 82, 127, };
const AudioSynthWavetable::instrument_data MutedTrumpet = {3, MutedTrumpet_ranges, MutedTrumpet_samples };

View file

@ -1,10 +1,6 @@
// Playtune bytestream for file "..\..\Downloads\Good Midi\Overworld.mid" created by MIDITONES V1.14 on Sun Mar 5 00:09:03 2017
// command line: miditones.exe -t16 -v -i ..\..\Downloads\Good Midi\Overworld
#ifdef __AVR__
#include <avr/pgmspace.h>
#else
#define PROGMEM
#endif
#include <Arduino.h>
const unsigned char PROGMEM score [] = {
0xC0,48, 0x90,66,127, 0xC1,48, 0x91,62,127, 0xC2,48, 0x92,38,127, 0xC3,48, 0x93,38,127, 0,222, 0x92,45,127, 0,222,
0x92,50,127, 0,222, 0x92,38,127, 0x93,52,127, 1,188, 0x93,50,127, 0,222, 0x93,50,127, 0,222, 0x92,33,127,

View file

@ -106,6 +106,7 @@ const AudioSynthWavetable::sample_data Pizzicato_samples[4] = {
},
};
PROGMEM
const uint32_t sample_0_Pizzicato_PizzViolinE3[2944] = {
0xffeeffbc,0x03230104,0x062004f3,0x0d4f07cb,0x1c351736,0x16ae18bd,0x1cad1a19,0x1ed81ede,
0x1a1b1b79,0x1ea61c1e,0x11081a2f,0x074a0a00,0x034305c7,0x012e02b7,0xf781fc58,0xf5d7f770,
@ -468,6 +469,7 @@ const uint32_t sample_0_Pizzicato_PizzViolinE3[2944] = {
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
const uint32_t sample_1_Pizzicato_PizzViolinC4[2304] = {
0x02770000,0x0b55063e,0x109c0f35,0x0d690fda,0x088109dd,0xfea10525,0xea5bf55e,0xec27e61c,
0xf843f547,0xebc1f337,0xeb4be81f,0xf9dcf398,0xf90ffa32,0xfe29faae,0x043a0215,0xf9d20247,
@ -760,6 +762,7 @@ const uint32_t sample_1_Pizzicato_PizzViolinC4[2304] = {
};
PROGMEM
const uint32_t sample_2_Pizzicato_PizzViolinE5[768] = {
0x00330000,0x0c1e0957,0x100c0a13,0xefb7085e,0xef13e447,0x25e11a69,0x23032238,0xc02ed183,
0xe93ddc07,0x0e2f1cb4,0x0a35fcfa,0xe333f478,0xee61d669,0x14351329,0x34ef33f1,0xd8cee3ff,
@ -851,6 +854,7 @@ const uint32_t sample_2_Pizzicato_PizzViolinE5[768] = {
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
const uint32_t sample_3_Pizzicato_PizzViolinE5[768] = {
0x00330000,0x0c1e0957,0x100c0a13,0xefb7085e,0xef13e447,0x25e11a69,0x23032238,0xc02ed183,
0xe93ddc07,0x0e2f1cb4,0x0a35fcfa,0xe333f478,0xee61d669,0x14351329,0x34ef33f1,0xd8cee3ff,

View file

@ -210,6 +210,7 @@ const AudioSynthWavetable::sample_data Viola_samples[8] = {
},
};
PROGMEM
const uint32_t sample_0_Viola_ViolinBb2[768] = {
0x00c00062,0x01d00177,0x00d901a3,0xfd26ff1b,0xfbfffc14,0xfcc2fc94,0xfbf4fc33,0xfdbcfca6,
0xfe03fe3f,0xfe96fddc,0x035100ee,0x035d0423,0x002c01be,0x00e00025,0x002f0108,0xff46ff54,
@ -299,6 +300,7 @@ const uint32_t sample_0_Viola_ViolinBb2[768] = {
0x00000000,0x00000000,
};
PROGMEM
const uint32_t sample_1_Viola_ViolinD3[896] = {
0xf22af42c,0xf477f50d,0x02bbf927,0x0e000a05,0x143e1088,0x15ac15b8,0x11f914ba,0x12870f75,
0x17fa1743,0x0d0c1332,0x07180989,0xfdef036b,0xfc75f893,0x0a50048f,0x0761093c,0x0e180a9b,
@ -405,6 +407,7 @@ const uint32_t sample_1_Viola_ViolinD3[896] = {
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
const uint32_t sample_2_Viola_ViolinG3[768] = {
0xff4afeb7,0xfeb0fed3,0xff55ff4a,0xffbeff92,0xffd9fff0,0x000f0001,0xfff0000b,0x00750031,
0x00be00e0,0x017f009a,0x020e01c7,0x025d024c,0x031002b9,0x036e02ec,0x038e0306,0x03420271,
@ -504,6 +507,7 @@ const uint32_t sample_2_Viola_ViolinG3[768] = {
0xed98fa3f,0x0000dd29,0x00000000,0x00000000,0x00000000,
};
PROGMEM
const uint32_t sample_3_Viola_ViolinC4[768] = {
0x02720224,0x01790179,0x015b010f,0x023001a2,0x02dc0298,0x01d50280,0x00970141,0x011300f4,
0x00be00f3,0xffe3002b,0xffbdffdf,0x00560035,0x007900af,0xffd30046,0xff02fef0,0xfed0feb6,
@ -595,6 +599,7 @@ const uint32_t sample_3_Viola_ViolinC4[768] = {
};
PROGMEM
const uint32_t sample_4_Viola_ViolinGb4[768] = {
0x0014001b,0x00150027,0x00390028,0x00450027,0x00120028,0x001b0022,0xffecfffe,0xffe6ffe0,
0xffeefffd,0x00370014,0x0093fff8,0xff980066,0x02d90135,0xf8f6052d,0xf60cf32b,0xfefdf844,
@ -685,6 +690,7 @@ const uint32_t sample_4_Viola_ViolinGb4[768] = {
0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
const uint32_t sample_5_Viola_ViolinC5[640] = {
0x01eafdb7,0xf3c6fadb,0xf8bef4e7,0x0c1aff72,0x15b51664,0x12ce16b5,0x0f101140,0x0b890d0a,
0x0aaa0be0,0x1905125c,0x2b9824a1,0x14a51f92,0xe92efcc9,0xe20ae403,0xde50e318,0xd902def2,
@ -767,6 +773,7 @@ const uint32_t sample_5_Viola_ViolinC5[640] = {
0x1659239f,0x13282024,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
const uint32_t sample_6_Viola_ViolinEb5[768] = {
0xff93feb8,0xfde4fb64,0xfe2dfc67,0x12fafe5e,0x120d13e5,0x00ca05cc,0x076307c9,0x0f5f06b9,
0x08080e4a,0x0f820d05,0x1c4813ad,0x0f4413f8,0xf9d10825,0xef44ec7d,0xeda5ec23,0xe7b9ed75,
@ -863,6 +870,7 @@ const uint32_t sample_6_Viola_ViolinEb5[768] = {
0x00000000,0x00000000,
};
PROGMEM
const uint32_t sample_7_Viola_ViolinEb6[512] = {
0xfde7ff2e,0xffebfcf6,0x09180f77,0x066b0250,0x07fe09b7,0x12670b88,0xfd950d9b,0xf116f10d,
0xe96aef60,0xf60fe23f,0x129212e1,0xf943fe40,0xf013f48b,0xf7b2f480,0x000afda7,0x0d25014e,

View file

@ -1,5 +1,6 @@
#include "WT_Trumpet_samples.h"
PROGMEM
static const uint32_t sample_0_WT_Trumpet_trum04[10112] = {
0xff9cff8f,0xffceffa9,0xfff90000,0xffd7ffe9,0xffbcffb2,0xff72ffa4,0xffc4ff9e,0xffbcffb2,
0xffdcffca,0x006a001e,0x012600c2,0x01620159,0x01430152,0x014e014d,0x0118013c,0x00b100e5,
@ -1265,6 +1266,7 @@ static const uint32_t sample_0_WT_Trumpet_trum04[10112] = {
0x07f2f8b2,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
PROGMEM
static const uint32_t sample_1_WT_Trumpet_trum09[9728] = {
0x0f1f0a8f,0x15811318,0x0d67137e,0xfeec05c4,0xf883fa6c,0xf8c9f836,0xfb84fa14,0xfe46fceb,
0xfeb1fee5,0xfe30fe62,0xfd05fdc9,0xfc17fc35,0xfbcafc52,0xf9c7fac5,0xf77af88a,0xf6e0f6d9,
@ -2534,4 +2536,3 @@ static const AudioSynthWavetable::sample_data WT_Trumpet_samples[2] = {
static const uint8_t WT_Trumpet_ranges[] = {70, 127, };
const AudioSynthWavetable::instrument_data WT_Trumpet = {2, WT_Trumpet_ranges, WT_Trumpet_samples };

View file

@ -0,0 +1,5 @@
miditones: miditones.c
gcc -O2 -Wall -o miditones miditones.c
clean:
rm -f miditones

View file

@ -0,0 +1,991 @@
/*********************************************************************************
*
* MIDITONES
*
* Convert a MIDI file into a bytestream of notes
*
*
* (C) Copyright 2011,2013,2015,2016 Len Shustek
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of version 3 of the GNU General Public License as
* published by the Free Software Foundation at http://www.gnu.org/licenses,
* with Additional Permissions under term 7(b) that the original copyright
* notice and author attibution must be preserved and under term 7(c) that
* modified versions be marked as different from the original.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
***********************************************************************************/
/*
* Change log
* 19 January 2011, L.Shustek, V1.0
* -Initial release.
* 26 February 2011, L. Shustek, V1.1
* -Expand the documentation generated in the output file.
* -End the binary output file with an "end of score" command.
* -Fix bug: Some "stop note" commands were generated too early.
* 04 March 2011, L. Shustek, V1.2
* -Minor error message rewording.
* 13 June 2011, L. Shustek, V1.3
* -Add -s2 strategy to try to keep each track on its own tone generator
* for when there are separate speakers. This obviously works only when
* each track is monophonic. (Suggested by Michal Pustejovsky)
* 20 November 2011, L. Shustek, V1.4
* -Add -cn option to mask which channels (tracks) to process
* -Add -kn option to change key
* Both of these are in support of music-playing on my Tesla Coil.
* 05 December 2011, L. Shustek, V1.5
* -Fix command line parsing error for option -s1
* -Display the commandline in the C file output
* -Change to decimal instead of hex for note numbers in the C file output
* 06 August 2013, L. Shustek, V1.6
* -Changed to allow compilation and execution in 64-bit environments
* by using C99 standard intN_t and uintN_t types for MIDI structures,
* and formatting specifications like "PRId32" instead of "ld".
* 04 April 2015, L. Shustek, V1.7
* -Made friendlier to other compilers: import source of strlcpy and strlcat,
* fixed various type mismatches that the LCC compiler didn't fret about.
* Generate "const" for data initialization for compatibility with Arduino IDE v1.6.x.
* 23 January 2016, D. Blackketter, V1.8
* -Fix warnings and errors building on Mac OS X via "gcc miditones.c"
* 25 January 2016, D. Blackketter, Paul Stoffregen, V1.9
-Merge in velocity output option from Arduino/Teensy Audio Library
*/
#define VERSION "1.9"
/*--------------------------------------------------------------------------------
*
*
* About MIDITONES
*
*
* MIDITONES converts a MIDI music file into a much simplified stream of commands,
* so that a version of the music can be played on a synthesizer having only
* tone generators without any volume or tone controls.
*
* Volume ("velocity") and instrument specifications in the MIDI files are discarded.
* All the tracks are prcoessed and merged into a single time-ordered stream of
* "note on", "note off", and "delay" commands.
*
* This was written for the "Playtune" Arduino library, which plays polyphonic music
* using up to 6 tone generators run by the timers on the processor. See the separate
* documentation for Playtune. But MIDITONES may prove useful for other tone
* generating systems.
*
* The output can be either a C-language source code fragment that initializes an
* array with the command bytestream, or a binary file with the bytestream itself.
*
* MIDITONES is written in standard ANSI C (plus strlcpy and strlcat functions), and
* is meant to be executed from the command line. There is no GUI interface.
*
* The MIDI file format is complicated, and this has not been tested on a very
* wide variety of file types. In particular, we have tested only format type "1",
* which seems to be what most of them are. Let me know if you find MIDI files
* that it won't digest and I'll see if I can fix it.
* This has been tested only on a little-endian PC, but I think it should work on
* big-endian processors too. Note that the MIDI file format is inherently
* big-endian.
*
*
* ***** The command line *****
*
* To convert a MIDI file called "chopin.mid" into a command bytestream, execute
*
* miditones chopin
*
* It will create a file in the same directory called "chopin.c" which contains
* the C-language statement to intiialize an array called "score" with the bytestream.
*
*
* The general form for command line execution is this:
*
* miditones [-p] [-lg] [-lp] [-s1] [-tn] [-b] [-cn] [-kn] <basefilename>
*
* The <basefilename> is the base name, without an extension, for the input and
* output files. It can contain directory path information, or not.
*
* The input file is the base name with the extension ".mid". The output filename(s)
* are the base name with ".c", ".bin", and/or ".log" extensions.
*
*
* The following command-line options can be specified:
*
* -p Only parse the MIDI file; don't generate an output file.
* Tracks are processed sequentially instead of being merged into chronological order.
* This is mostly useful when generating a log to debug MIDI file parsing problems.
*
* -lp Log input file parsing information to the <basefilename>.log file
*
* -lg Log output bytestream generation information to the <basefilename>.log file
*
* -sn Use bytestream generation strategy "n".
* Two strategies are currently implemented:
* 1: favor track 1 notes instead of all tracks equally
* 2: try to keep each track to its own tone generator
*
* -tn Generate the bytestream so that at most n tone generators are used.
* The default is 6 tone generators, and the maximum is 16.
* The program will report how many notes had to be discarded because there
* weren't enough tone generators. Note that for the Arduino Playtunes
* library, it's ok to have the bytestream use more tone genreators than
* exist on your processor because any extra notes will be ignored, although
* it does make the file bigger than necessary . Of course, too many ignored
* notes will make the music sound really strange!
*
* -b Generate a binary file with the name <basefilename>.bin, instead of a
* C-language source file with the name <basefilename>.c.
*
* -cn Only process the channel numbers whose bits are on in the number "n".
* For example, -c3 means "only process channels 0 and 1"
*
* -kn Change the musical key of the output by n chromatic notes.
* -k-12 goes one octave down, -k12 goes one octave up, etc.
*
* -v Add velocity information to output
*
*
* ***** The score bytestream *****
*
* The generated bytestream is a series of commands that turn notes on and off, and
* start delays until the next note change. Here are the details, with numbers
* shown in hexadecimal.
*
* If the high-order bit of the byte is 1, then it is one of the following commands:
*
* 9t nn [vv] Start playing note nn on tone generator t. Generators are numbered
* starting with 0. The notes numbers are the MIDI numbers for the chromatic
* scale, with decimal 60 being Middle C, and decimal 69 being Middle A (440 Hz).
* if the -v option is enabled, a second byte is added to indicate velocity
*
* 8t Stop playing the note on tone generator t.
*
* F0 End of score: stop playing.
*
* E0 End of score: start playing again from the beginning.
* (Shown for completeness; MIDITONES won't generate this.)
*
* If the high-order bit of the byte is 0, it is a command to delay for a while until
* the next note change.. The other 7 bits and the 8 bits of the following byte are
* interpreted as a 15-bit big-endian integer that is the number of milliseconds to
* wait before processing the next command. For example,
*
* 07 D0
*
* would cause a delay of 0x07d0 = 2000 decimal millisconds, or 2 seconds. Any tones
* that were playing before the delay command will continue to play.
*
*
* Len Shustek, 4 Feb 2011
*
*----------------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <time.h>
#include <inttypes.h>
/*********** MIDI file header formats *****************/
struct midi_header {
int8_t MThd[4];
uint32_t header_size;
uint16_t format_type;
uint16_t number_of_tracks;
uint16_t time_division;
};
struct track_header {
int8_t MTrk[4];
uint32_t track_size;
};
/*********** Global variables ******************/
#define MAX_TONEGENS 16 /* max tone generators: tones we can play simultaneously */
#define DEFAULT_TONEGENS 6 /* default number of tone generators */
#define MAX_TRACKS 24 /* max number of MIDI tracks we will process */
bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, velocityoutput;
FILE *infile, *outfile, *logfile;
uint8_t *buffer, *hdrptr;
unsigned long buflen;
int num_tracks;
int tracks_done = 0;
int outfile_itemcount = 0;
int num_tonegens = DEFAULT_TONEGENS;
int num_tonegens_used = 0;
unsigned channel_mask = 0xffff; // bit mask of channels to process
int keyshift = 0; // optional chromatic note shift for output file
long int outfile_bytecount = 0;
unsigned int ticks_per_beat = 240;
unsigned long timenow = 0;
unsigned long tempo; /* current tempo in usec/qnote */
struct tonegen_status { /* current status of a tone generator */
bool playing; /* is it playing? */
int track; /* if so, which track is the note from? */
int note; /* what note is playing? */
}
tonegen [MAX_TONEGENS] = {
{0}};
struct track_status { /* current processing point of a MIDI track */
uint8_t *trkptr; /* ptr to the next note change */
uint8_t *trkend; /* ptr past the end of the track */
unsigned long time; /* what time we're at in the score */
unsigned long tempo; /* the tempo last set, in usec/qnote */
unsigned int preferred_tonegen; /* for strategy2: try to use this generator */
unsigned char cmd; /* CMD_xxxx next to do */
unsigned char note; /* for which note */
unsigned char velocity;
unsigned char last_event; /* the last event, for MIDI's "running status" */
bool tonegens[MAX_TONEGENS];/* which tone generators our notes are playing on */
}
track[MAX_TRACKS] = {
{0}};
/* output bytestream commands, which are also stored in track_status.cmd */
#define CMD_PLAYNOTE 0x90 /* play a note: low nibble is generator #, note is next byte */
#define CMD_STOPNOTE 0x80 /* stop a note: low nibble is generator # */
#define CMD_RESTART 0xe0 /* restart the score from the beginning */
#define CMD_STOP 0xf0 /* stop playing */
/* if CMD < 0x80, then the other 7 bits and the next byte are a 15-bit number of msec to delay */
/* these other commands stored in the track_status.com */
#define CMD_TEMPO 0xFE /* tempo in usec per quarter note ("beat") */
#define CMD_TRACKDONE 0xFF /* no more data left in this track */
/************** command-line processing *******************/
void SayUsage(char *programName){
static char *usage[] = {
"Convert MIDI files to an Arduino PLAYTUNE bytestream",
"miditones [-p] [-lg] [-lp] [-s1] [-tn] [-v] <basefilename>",
" -p parse only, don't generate bytestream",
" -lp log input parsing",
" -lg log output generation",
" -s1 strategy 1: favor track 1",
" -s2 strategy 2: try to assign tracks to specific tone generators",
" -tn use at most n tone generators (default is 6, max is 16)",
" -b binary file output instead of C source text",
" -cn mask for which tracks to process, e.g. -c3 for only 0 and 1",
" -kn key shift in chromatic notes, positive or negative",
" -v include velocity data in play note commands",
"input file: <basefilename>.mid",
"output file: <basefilename>.bin or .c",
"log file: <basefilename>.log",
"" };
int i=0;
while (usage[i][0] != '\0') fprintf(stderr, "%s\n", usage[i++]);
}
int HandleOptions(int argc,char *argv[]) {
/* returns the index of the first argument that is not an option; i.e.
does not start with a dash or a slash*/
int i,firstnonoption=0;
/* --- The following skeleton comes from C:\lcc\lib\wizard\textmode.tpl. */
for (i=1; i< argc;i++) {
if (argv[i][0] == '/' || argv[i][0] == '-') {
switch (toupper(argv[i][1])) {
case 'H':
case '?':
SayUsage(argv[0]);
exit(1);
case 'L':
if (toupper(argv[i][2]) == 'G') loggen = true;
else if (toupper(argv[i][2]) == 'P') logparse = true;
else goto opterror;
break;
case 'P':
parseonly = true;
break;
case 'B':
binaryoutput = true;
break;
case 'V':
velocityoutput = true;
break;
case 'S':
if (argv[i][2] == '1') strategy1 = true;
else if (argv[i][2] == '2') strategy2 = true;
else goto opterror;
break;
case 'T':
if (sscanf(&argv[i][2],"%d",&num_tonegens) != 1 || num_tonegens <1 || num_tonegens > MAX_TONEGENS) goto opterror;
printf("Using %d tone generators.\n", num_tonegens);
break;
case 'C':
if (sscanf(&argv[i][2],"%d",&channel_mask) != 1 || channel_mask > 0xffff) goto opterror;
printf("Channel (track) mask is %04X.\n", channel_mask);
break;
case 'K':
if (sscanf(&argv[i][2],"%d",&keyshift) != 1 || keyshift < -100 || keyshift > 100) goto opterror;
printf("Using keyshift %d.\n", keyshift);
break;
/* add more option switches here */
opterror:
default:
fprintf(stderr,"unknown option: %s\n",argv[i]);
SayUsage(argv[0]);
exit(4);
}
}
else {
firstnonoption = i;
break;
}
}
return firstnonoption;
}
void print_command_line (int argc,char *argv[]) {
int i;
fprintf(outfile, "// command line: ");
for (i=0; i< argc; i++) fprintf(outfile,"%s ",argv[i]);
fprintf(outfile, "\n");
}
/**************** utility routines **********************/
/* safe string copy */
size_t miditones_strlcpy(char *dst, const char *src, size_t siz) {
char *d = dst;
const char *s = src;
size_t n = siz;
/* Copy as many bytes as will fit */
if (n != 0)
{
while (--n != 0)
{
if ((*d++ = *s++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src */
if (n == 0)
{
if (siz != 0)
*d = '\0'; /* NUL-terminate dst */
while (*s++)
;
}
return (s - src - 1); /* count does not include NUL */
}
/* safe string concatenation */
size_t miditones_strlcat(char *dst, const char *src, size_t siz) {
char *d = dst;
const char *s = src;
size_t n = siz;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end */
while (n-- != 0 && *d != '\0')
d++;
dlen = d - dst;
n = siz - dlen;
if (n == 0)
return (dlen + strlen(s));
while (*s != '\0')
{
if (n != 1)
{
*d++ = *s;
n--;
}
s++;
}
*d = '\0';
return (dlen + (s - src)); /* count does not include NUL */
}
/* match a constant character sequence */
int charcmp (const char *buf, const char *match) {
int len, i;
len = strlen (match);
for (i=0; i<len; ++i)
if (buf[i] != match[i]) return 0;
return 1;
}
/* announce a fatal MIDI file format error */
void midi_error (char *msg, unsigned char *bufptr) {
unsigned char *ptr;
fprintf(stderr, "---> MIDI file error at position %04X (%d): %s\n", (uint16_t)(bufptr-buffer), (uint16_t)(bufptr-buffer), msg);
/* print some bytes surrounding the error */
ptr = bufptr - 16;
if (ptr < buffer) ptr = buffer;
for (; ptr <= bufptr+16 && ptr < buffer+buflen; ++ptr) fprintf (stderr, ptr==bufptr ? " [%02X] ":"%02X ", *ptr);
fprintf(stderr, "\n");
exit(8);
}
/* check that we have a specified number of bytes left in the buffer */
void chk_bufdata(unsigned char *ptr, unsigned long int len) {
if ((unsigned)(ptr + len - buffer) > buflen) midi_error("data missing", ptr);
}
/* fetch big-endian numbers */
uint16_t rev_short (uint16_t val) {
return ((val&0xff)<<8) | ((val>>8)&0xff);
}
uint32_t rev_long (uint32_t val){
return (((rev_short((uint16_t)val) & 0xffff) << 16) |
(rev_short((uint16_t)(val >> 16)) & 0xffff));
}
/* account for new items in the non-binary output file
and generate a newline every so often. */
void outfile_items (int n) {
outfile_bytecount += n;
outfile_itemcount += n;
if (!binaryoutput && outfile_itemcount > 20) {
fprintf (outfile, "\n");
outfile_itemcount = 0;
}
}
/************** process the MIDI file header *****************/
void process_header (void) {
struct midi_header *hdr;
unsigned int time_division;
chk_bufdata(hdrptr, sizeof(struct midi_header));
hdr = (struct midi_header *) hdrptr;
if (!charcmp((char*)hdr->MThd,"MThd")) midi_error("Missing 'MThd'", hdrptr);
num_tracks = rev_short(hdr->number_of_tracks);
time_division = rev_short(hdr->time_division);
if (time_division < 0x8000) ticks_per_beat = time_division;
else ticks_per_beat = ((time_division >> 8) & 0x7f) /* SMTE frames/sec */ * (time_division & 0xff); /* ticks/SMTE frame */
if (logparse) {
fprintf (logfile, "Header size %" PRId32 "\n", rev_long(hdr->header_size));
fprintf (logfile, "Format type %d\n", rev_short(hdr->format_type));
fprintf (logfile, "Number of tracks %d\n", num_tracks);
fprintf (logfile, "Time division %04X\n", time_division);
fprintf (logfile, "Ticks/beat = %d\n", ticks_per_beat);
}
hdrptr += rev_long(hdr->header_size) + 8; /* point past header to track header, presumably. */
return;
}
/**************** Process a MIDI track header *******************/
void start_track (int tracknum) {
struct track_header *hdr;
unsigned long tracklen;
chk_bufdata(hdrptr, sizeof(struct track_header));
hdr = (struct track_header *) hdrptr;
if (!charcmp((char *)(hdr->MTrk),"MTrk")) midi_error("Missing 'MTrk'", hdrptr);
tracklen = rev_long(hdr->track_size);
if (logparse) fprintf (logfile, "\nTrack %d length %ld\n", tracknum, tracklen);
hdrptr += sizeof (struct track_header); /* point past header */
chk_bufdata(hdrptr, tracklen);
track[tracknum].trkptr = hdrptr;
hdrptr += tracklen; /* point to the start of the next track */
track[tracknum].trkend = hdrptr; /* the point past the end of the track */
}
/* Get a MIDI-style variable-length integer */
unsigned long get_varlen (uint8_t **ptr) {
/* Get a 1-4 byte variable-length value and adjust the pointer past it.
These are a succession of 7-bit values with a MSB bit of zero marking the end */
unsigned long val;
int i, byte;
val = 0;
for (i=0; i<4; ++i){
byte = *(*ptr)++;
val = (val<<7) | (byte&0x7f);
if (!(byte&0x80)) return val;
}
return val;
}
/*************** Process the MIDI track data ***************************/
/* Skip in the track for the next "note on", "note off" or "set tempo" command,
then record that information in the track status block and return. */
void find_note (int tracknum) {
unsigned long int delta_time;
int event, chan;
int i;
int note, velocity;
int meta_cmd, meta_length;
unsigned long int sysex_length;
struct track_status *t;
/* process events */
t = &track[tracknum]; /* our track status structure */
while (t->trkptr < t->trkend) {
delta_time = get_varlen(&t->trkptr);
if (logparse) {
fprintf (logfile, "trk %d ", tracknum);
if (delta_time) {
fprintf (logfile, "delta time %4ld, ", delta_time);
} else {
fprintf (logfile, " ");
}
}
t->time += delta_time;
if (*t->trkptr < 0x80) /* "running status" */ event = t->last_event;/* means same event as before */
else { /* new "status" (event type) */
event = *t->trkptr++;
t->last_event = event;
}
if (event == 0xff) { /* meta-event */
meta_cmd = *t->trkptr++;
meta_length = *t->trkptr++;
switch (meta_cmd) {
case 0x2f:
if (logparse) fprintf(logfile, "end of track\n");
break;
case 0x00:
if (logparse) fprintf(logfile, "sequence number %d\n", rev_short(*(unsigned short *)t->trkptr));
break;
case 0x20:
if (logparse) fprintf(logfile, "channel prefix %d\n", *t->trkptr);
break;
case 0x51: /* tempo: 3 byte big-endian integer! */
t->cmd = CMD_TEMPO;
t->tempo = rev_long(*(unsigned long *)(t->trkptr-1)) & 0xffffffL;
if (logparse) fprintf(logfile, "set tempo %ld usec/qnote\n", t->tempo);
t->trkptr += meta_length;
return;
case 0x54:
if (logparse) fprintf(logfile, "SMPTE offset %08" PRIx32 "\n", rev_long(*(unsigned long *)t->trkptr));
break;
case 0x58:
if (logparse) fprintf(logfile, "time signature %08" PRIx32 "\n", rev_long(*(unsigned long *)t->trkptr));
break;
case 0x59:
if (logparse) fprintf(logfile, "key signature %04X\n", rev_short(*(unsigned short *)t->trkptr));
break;
default: /* assume it is a string */
if (logparse) {
fprintf(logfile, "meta cmd %02X, length %d, \"", meta_cmd, meta_length);
for (i=0; i<meta_length; ++i) {
int ch = t->trkptr[i];
fprintf(logfile, "%c", isprint(ch) ? ch : '?');
}
fprintf(logfile, "\"\n");
}
if (tracknum==0 && meta_cmd==0x03 && !parseonly && !binaryoutput) {
/* Incredibly, MIDI has no standard for recording the name of the piece!
Track 0's "trackname" (meta 0x03) is sometimes used for that, so
we output it to the C file as documentation. */
fprintf(outfile, "// ");
for (i=0; i<meta_length; ++i) {
int ch = t->trkptr[i];
fprintf(outfile, "%c", isprint(ch) ? ch : '?');
}
fprintf(outfile, "\n");
}
break;
}
t->trkptr += meta_length;
}
else if (event <0x80) midi_error("Unknown MIDI event type", t->trkptr);
else {
chan = event & 0xf;
switch (event>>4) {
case 0x8:
t->note = *t->trkptr++;
velocity = *t->trkptr++;
note_off:
if (logparse) fprintf (logfile, "note %02X off, chan %d, velocity %d\n", t->note, chan, velocity);
if ((1<<chan) & channel_mask) { // if we're processing this channel
t->cmd = CMD_STOPNOTE;
return; /* stop processing and return */
}
break; // else keep looking
case 0x9:
t->note = *t->trkptr++;
velocity = *t->trkptr++;
if (velocity == 0) /* some scores use note-on with zero velocity for off! */ goto note_off;
t->velocity = velocity;
if (logparse) fprintf (logfile, "note %02X on, chan %d, velocity %d\n", t->note, chan, velocity);
if ((1<<chan) & channel_mask) { // if we're processing this channel
t->cmd = CMD_PLAYNOTE;
return; /* stop processing and return */
}
break; // else keep looking
case 0xa:
note = *t->trkptr++;
velocity = *t->trkptr++;
if (logparse) fprintf (logfile, "after-touch %02X, %02X\n", note, velocity);
break;
case 0xb:
note = *t->trkptr++;
velocity = *t->trkptr++;
if (logparse) fprintf (logfile, "control change %02X, %02X\n", note, velocity);
break;
case 0xc:
note = *t->trkptr++;
if (logparse) fprintf(logfile, "program patch %02X\n", note);
break;
case 0xd:
chan = *t->trkptr++;
if (logparse) fprintf(logfile, "channel after-touch %02X\n", chan);
break;
case 0xe:
note = *t->trkptr++;
velocity = *t->trkptr++;
if (logparse) fprintf(logfile, "pitch wheel change %02X, %02X\n", note, velocity);
break;
case 0xf:
sysex_length = get_varlen(&t->trkptr);
if (logparse) fprintf(logfile, "SysEx event %02X, %ld bytes\n", event, sysex_length);
t->trkptr += sysex_length;
break;
default:
midi_error("Unknown MIDI command", t->trkptr);
}
}
}
t->cmd = CMD_TRACKDONE; /* no more notes to process */
++tracks_done;
}
/********************* main ****************************/
int main(int argc,char *argv[]) {
int argno;
char *filebasename;
#define MAXPATH 120
char filename[MAXPATH];
int tracknum;
int earliest_tracknum;
unsigned long earliest_time;
int notes_skipped = 0;
printf("MIDITONES V%s, (C) 2011,2015,2016 Len Shustek\n", VERSION);
printf("See the source code for license information.\n\n");
if (argc == 1) { /* no arguments */
SayUsage(argv[0]);
return 1;
}
/* process options */
argno = HandleOptions(argc,argv);
filebasename = argv[argno];
/* Open the log file */
if (logparse || loggen) {
miditones_strlcpy(filename, filebasename, MAXPATH);
miditones_strlcat(filename, ".log", MAXPATH);
logfile = fopen(filename, "w");
if (!logfile) {
fprintf(stderr, "Unable to open log file %s", filename);
return 1;
}
}
/* Open the input file */
miditones_strlcpy(filename, filebasename, MAXPATH);
miditones_strlcat(filename, ".mid", MAXPATH);
infile = fopen(filename, "rb");
if (!infile) {
fprintf(stderr, "Unable to open input file %s", filename);
return 1;
}
/* Read the whole input file into memory */
fseek(infile, 0, SEEK_END); /* find file size */
buflen = ftell(infile);
fseek(infile, 0, SEEK_SET);
buffer = (unsigned char *) malloc (buflen+1);
if (!buffer) {
fprintf(stderr, "Unable to allocate %ld bytes for the file", buflen);
return 1;
}
fread(buffer, buflen, 1, infile);
fclose(infile);
if (logparse) fprintf(logfile, "Processing %s, %ld bytes\n", filename, buflen);
/* Create the output file */
if (!parseonly) {
miditones_strlcpy(filename, filebasename, MAXPATH);
if (binaryoutput) {
miditones_strlcat(filename, ".bin", MAXPATH);
outfile = fopen(filename, "wb");
}
else {
miditones_strlcat(filename, ".c", MAXPATH);
outfile = fopen(filename, "w");
}
if (!outfile) {
fprintf(stderr, "Unable to open output file %s", filename);
return 1;
}
if (!binaryoutput) { /* create header of C file that initializes score data */
time_t rawtime;
time (&rawtime);
fprintf(outfile, "// Playtune bytestream for file \"%s.mid\" ", filebasename);
fprintf(outfile, "created by MIDITONES V%s on %s", VERSION, asctime(localtime(&rawtime)));
print_command_line(argc,argv);
if (channel_mask != 0xffff)
fprintf(outfile, "// Only the masked channels were processed: %04X\n", channel_mask);
if (keyshift != 0)
fprintf(outfile, "// Keyshift was %d chromatic notes\n", keyshift);
fprintf(outfile, "#ifdef __AVR__\n");
fprintf(outfile, "#include <avr/pgmspace.h>\n");
fprintf(outfile, "#else\n");
fprintf(outfile, "#define PROGMEM\n");
fprintf(outfile, "#endif\n");
fprintf(outfile, "const unsigned char PROGMEM score [] = {\n");
}
}
/* process the MIDI file header */
hdrptr = buffer; /* pointer to file and track headers */
process_header ();
printf (" Processing %d tracks.\n", num_tracks);
if (num_tracks > MAX_TRACKS) midi_error ("Too many tracks", buffer);
/* initialize processing of all the tracks */
for (tracknum=0; tracknum < num_tracks; ++tracknum) {
start_track (tracknum); /* process the track header */
find_note (tracknum); /* position to the first note on/off */
/* if we are in "parse only" mode, do the whole track,
so we do them one at a time instead of time-synchronized. */
if (parseonly) while (track[tracknum].cmd != CMD_TRACKDONE) find_note(tracknum);
}
/* Continue processing all tracks, in an order based on the simulated time.
This is not unlike multiway merging used for tape sorting algoritms in the 50's! */
tracknum = 0;
if (!parseonly) do { /* while there are still track notes to process */
struct track_status *trk;
struct tonegen_status *tg;
int tgnum;
int count_tracks;
unsigned long delta_time, delta_msec;
/* Find the track with the earliest event time,
and output a delay command if time has advanced.
A potential improvement: If there are multiple tracks with the same time,
first do the ones with STOPNOTE as the next command, if any. That would
help avoid running out of tone generators. In practice, though, most MIDI
files do all the STOPNOTEs first anyway, so it won't have much effect.
*/
earliest_time = 0x7fffffff;
/* Usually we start with the track after the one we did last time (tracknum),
so that if we run out of tone generators, we have been fair to all the tracks.
The alternate "strategy1" says we always start with track 0, which means
that we favor early tracks over later ones when there aren't enough tone generators.
*/
count_tracks = num_tracks;
if (strategy1) tracknum = num_tracks; /* beyond the end, so we start with track 0 */
do {
if (++tracknum >= num_tracks) tracknum=0;
trk = &track[tracknum];
if (trk->cmd != CMD_TRACKDONE && trk->time < earliest_time) {
earliest_time = trk->time;
earliest_tracknum = tracknum;
}
}
while (--count_tracks);
tracknum = earliest_tracknum; /* the track we picked */
trk = &track[tracknum];
if (loggen) fprintf (logfile, "Earliest time is trk %d, time %ld\n", tracknum, earliest_time);
if (earliest_time < timenow) midi_error ("INTERNAL: time went backwards", trk->trkptr);
/* If time has advanced, output a "delay" command */
delta_time = earliest_time - timenow;
if (delta_time) {
/* Convert ticks to milliseconds based on the current tempo */
delta_msec = ((unsigned long) delta_time * tempo) / ticks_per_beat / 1000;
if (loggen) fprintf (logfile, "->Delay %ld msec (%ld ticks)\n", delta_msec, delta_time);
if (delta_msec > 0x7fff) midi_error ("INTERNAL: time delta too big", trk->trkptr);
/* output a 15-bit delay in big-endian format */
if (binaryoutput) {
putc ((unsigned char) (delta_msec >> 8), outfile);
putc ((unsigned char) (delta_msec & 0xff), outfile);
outfile_bytecount += 2;
}
else {
fprintf (outfile, "%ld,%ld, ", delta_msec >> 8, delta_msec & 0xff);
outfile_items(2);
}
}
timenow = earliest_time;
/* If this track event is "set tempo", just change the global tempo.
That affects how we generate "delay" commands. */
if (trk->cmd == CMD_TEMPO) {
tempo = trk->tempo;
if (loggen) fprintf (logfile, "Tempo changed to %ld usec/qnote\n", tempo);
find_note (tracknum);
}
/* If this track event is "stop note", process it and all subsequent "stop notes" for this track
that are happening at the same time. Doing so frees up as many tone generators as possible. */
else if (trk->cmd == CMD_STOPNOTE) do {
// stop a note
for (tgnum=0; tgnum < num_tonegens; ++tgnum) { /* find which generator is playing it */
tg = &tonegen[tgnum];
if (tg->playing && tg->track == tracknum && tg->note == trk->note) {
if (loggen) fprintf (logfile, "->Stop note %02X, generator %d, track %d\n", tg->note, tgnum, tracknum);
if (binaryoutput) {
putc (CMD_STOPNOTE | tgnum, outfile);
outfile_bytecount += 1;
}
else {
fprintf (outfile, "0x%02X, ", CMD_STOPNOTE | tgnum);
outfile_items (1);
}
tg->playing = false;
trk->tonegens[tgnum] = false;
}
}
find_note (tracknum); // use up the note
}
while (trk->cmd == CMD_STOPNOTE && trk->time == timenow);
/* If this track event is "start note", process only it.
Don't do more than one, so we allow other tracks their chance at grabbing tone generators. */
else if (trk->cmd == CMD_PLAYNOTE) {
bool foundgen = false;
if (strategy2) { /* try to use the same tone generator this track used last time */
tg = &tonegen [trk->preferred_tonegen];
if (!tg->playing) {
tgnum = trk->preferred_tonegen;
foundgen = true;
}
}
if (!foundgen) for (tgnum=0; tgnum < num_tonegens; ++tgnum) { /* search for a free tone generator */
tg = &tonegen[tgnum];
if (!tg->playing) {
foundgen = true;
break;
}
}
if (foundgen) {
int shifted_note;
if (tgnum+1 > num_tonegens_used) num_tonegens_used = tgnum+1;
tg->playing = true;
tg->track = tracknum;
tg->note = trk->note;
trk->tonegens[tgnum] = true;
trk->preferred_tonegen = tgnum;
if (loggen) fprintf (logfile, "->Start note %02X, generator %d, track %d\n", trk->note, tgnum, tracknum);
shifted_note = trk->note + keyshift;
if (shifted_note < 0) shifted_note = 0;
if (shifted_note > 127) shifted_note = 127;
if (binaryoutput) {
putc (CMD_PLAYNOTE | tgnum, outfile);
putc (shifted_note, outfile);
outfile_bytecount += 2;
if (velocityoutput) {
putc (trk->velocity, outfile);
outfile_bytecount++;
}
}
else {
if (velocityoutput == 0) {
fprintf (outfile, "0x%02X,%d, ", CMD_PLAYNOTE | tgnum, shifted_note);
outfile_items(2);
} else {
fprintf (outfile, "0x%02X,%d,%d, ", CMD_PLAYNOTE | tgnum, shifted_note, trk->velocity);
outfile_items(3);
}
}
}
else {
if (loggen) fprintf (logfile, "----> No free generator, skipping note %02X, track %d\n", trk->note, tracknum);
++notes_skipped;
}
find_note (tracknum); // use up the note
}
} /* !parseonly do */
while (tracks_done < num_tracks);
if (!parseonly) {
// generate the end-of-score command and some commentary
if(binaryoutput) putc(CMD_STOP, outfile);
else {
fprintf(outfile, "0x%02x};\n// This score contains %ld bytes, and %d tone generator%s used.\n", CMD_STOP, outfile_bytecount, num_tonegens_used, num_tonegens_used == 1 ? " is" : "s are");
if (notes_skipped) fprintf(outfile, "// %d notes had to be skipped.\n", notes_skipped);
}
printf (" %s %d tone generators were used.\n", num_tonegens_used < num_tonegens ? "Only":"All", num_tonegens_used);
if (notes_skipped) printf(" %d notes were skipped because there weren't enough tone generators.\n", notes_skipped);
printf (" %ld bytes of score data were generated.\n", outfile_bytecount);
}
printf (" Done.\n");
return 0;
}

View file

@ -0,0 +1,356 @@
// Convert a set of WAV audio files to C data arrays for the Teensy3 Audio Library
// Copyright 2014, Paul Stoffregen (paul@pjrc.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", 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.
// compile with: gcc -O2 -Wall -o wav2sketch wav2sketch.c
// i686-w64-mingw32-gcc -s -O2 -Wall wav2sketch.c -o wav2sketch.exe
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
uint8_t ulaw_encode(int16_t audio);
void print_byte(FILE *out, uint8_t b);
void filename2samplename(void);
uint32_t padding(uint32_t length, uint32_t block);
uint8_t read_uint8(FILE *in);
int16_t read_int16(FILE *in);
uint32_t read_uint32(FILE *in);
void die(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
// WAV file format:
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
const char *filename="";
char samplename[64];
unsigned int bcount, wcount;
unsigned int total_length=0;
int pcm_mode=0;
void wav2c(FILE *in, FILE *out, FILE *outh)
{
uint32_t header[4];
int16_t format, channels, bits;
uint32_t rate;
uint32_t i, length, padlength=0, arraylen;
uint32_t chunkSize;
int32_t audio=0;
// read the WAV file's header
for (i=0; i < 4; i++) {
header[i] = read_uint32(in);
}
while (header[3] != 0x20746D66) {
// skip past unknown sections until "fmt "
chunkSize = read_uint32(in);
for (i=0; i < chunkSize; i++) {
read_uint8(in);
}
header[3] = read_uint32(in);
}
chunkSize = read_uint32(in);
// read the audio format parameters
format = read_int16(in);
channels = read_int16(in);
rate = read_uint32(in);
read_uint32(in); // ignore byterate
read_int16(in); // ignore blockalign
bits = read_int16(in);
//printf("format: %d, channels: %d, rate: %d, bits %d\n", format, channels, rate, bits);
if (format != 1)
die("file %s is compressed, only uncompressed supported", filename);
if (rate != 44100 && rate != 22050 && rate != 11025 /*&& rate != 8000*/ )
die("sample rate %d in %s is unsupported\n"
"Only 44100, 22050, 11025 work", rate, filename);
if (channels != 1 && channels != 2)
die("file %s has %d channels, but only 1 & 2 are supported", filename, channels);
if (bits != 16)
die("file %s has %d bit format, but only 16 is supported", filename, bits);
// skip past any extra data on the WAVE header (hopefully it doesn't matter?)
for (chunkSize -= 16; chunkSize > 0; chunkSize--) {
read_uint8(in);
}
// read the data header, skip non-audio data
while (1) {
header[0] = read_uint32(in);
length = read_uint32(in);
if (header[0] == 0x61746164) break; // beginning of actual audio data
// skip over non-audio data
for (i=0; i < length; i++) {
read_uint8(in);
}
}
// the length must be a multiple of the data size
if (channels == 2) {
if (length % 4) die("file %s data length is not a multiple of 4", filename);
length = length / 4;
}
if (channels == 1) {
if (length % 1) die("file %s data length is not a multiple of 2", filename);
length = length / 2;
}
if (length > 0xFFFFFF) die("file %s data length is too long", filename);
bcount = 0;
// AudioPlayMemory requires padding to 2.9 ms boundary (128 samples @ 44100)
if (rate == 44100) {
padlength = padding(length, 128);
format = 1;
} else if (rate == 22050) {
padlength = padding(length, 64);
format = 2;
} else if (rate == 11025) {
padlength = padding(length, 32);
format = 3;
}
if (pcm_mode) {
arraylen = ((length + padlength) * 2 + 3) / 4 + 1;
format |= 0x80;
} else {
arraylen = (length + padlength + 3) / 4 + 1;
}
total_length += arraylen;
// output a minimal header, just the length, #bits and sample rate
fprintf(outh, "extern const unsigned int AudioSample%s[%d];\n", samplename, arraylen);
fprintf(out, "// Converted from %s, using %d Hz, %s encoding\n", filename, rate,
(pcm_mode ? "16 bit PCM" : "u-law"));
fprintf(out, "const unsigned int AudioSample%s[%d] = {\n", samplename, arraylen);
fprintf(out, "0x%08X,", length | (format << 24));
wcount = 1;
// finally, read the audio data
while (length > 0) {
if (channels == 1) {
audio = read_int16(in);
} else {
audio = read_int16(in);
audio += read_int16(in);
audio /= 2;
}
if (pcm_mode) {
print_byte(out, audio);
print_byte(out, audio >> 8);
} else {
print_byte(out, ulaw_encode(audio));
}
length--;
}
while (padlength > 0) {
print_byte(out, 0);
padlength--;
}
while (bcount > 0) {
print_byte(out, 0);
}
if (wcount > 0) fprintf(out, "\n");
fprintf(out, "};\n");
}
uint8_t ulaw_encode(int16_t audio)
{
uint32_t mag, neg;
// http://en.wikipedia.org/wiki/G.711
if (audio >= 0) {
mag = audio;
neg = 0;
} else {
mag = audio * -1;
neg = 0x80;
}
mag += 128;
if (mag > 0x7FFF) mag = 0x7FFF;
if (mag >= 0x4000) return neg | 0x70 | ((mag >> 10) & 0x0F); // 01wx yz00 0000 0000
if (mag >= 0x2000) return neg | 0x60 | ((mag >> 9) & 0x0F); // 001w xyz0 0000 0000
if (mag >= 0x1000) return neg | 0x50 | ((mag >> 8) & 0x0F); // 0001 wxyz 0000 0000
if (mag >= 0x0800) return neg | 0x40 | ((mag >> 7) & 0x0F); // 0000 1wxy z000 0000
if (mag >= 0x0400) return neg | 0x30 | ((mag >> 6) & 0x0F); // 0000 01wx yz00 0000
if (mag >= 0x0200) return neg | 0x20 | ((mag >> 5) & 0x0F); // 0000 001w xyz0 0000
if (mag >= 0x0100) return neg | 0x10 | ((mag >> 4) & 0x0F); // 0000 0001 wxyz 0000
return neg | 0x00 | ((mag >> 3) & 0x0F); // 0000 0000 1wxy z000
}
// compute the extra padding needed
uint32_t padding(uint32_t length, uint32_t block)
{
uint32_t extra;
extra = length % block;
if (extra == 0) return 0;
return block - extra;
}
// pack the output bytes into 32 bit words, lsb first, and
// format the data nicely with commas and newlines
void print_byte(FILE *out, uint8_t b)
{
static uint32_t buf32=0;
buf32 |= (b << (8 * bcount++));
if (bcount >= 4) {
fprintf(out, "0x%08X,", buf32);
buf32 = 0;
bcount = 0;
if (++wcount >= 8) {
fprintf(out, "\n");
wcount = 0;
}
}
}
// convert the WAV filename into a C-compatible name
void filename2samplename(void)
{
int len, i, n;
char c;
len = strlen(filename) - 4;
if (len >= sizeof(samplename)-1) len = sizeof(samplename)-1;
for (i=0, n=0; n < len; i++) {
c = filename[i];
if (isalpha(c) || c == '_' || (isdigit(c) && n > 0)) {
samplename[n] = (n == 0) ? toupper(c) : tolower(c);
n++;
}
}
samplename[n] = 0;
}
const char *title = "// Audio data converted from WAV file by wav2sketch\n\n";
int main(int argc, char **argv)
{
DIR *dir;
struct dirent *f;
struct stat s;
FILE *fp, *outc=NULL, *outh=NULL;
char buf[128];
int i, len;
// By default, audio is u-law encoded to reduce the memory requirement
// in half. However, u-law does add distortion. If "-16" is specified
// on the command line, the original 16 bit PCM samples are used.
for (i=1; i < argc; i++) {
if (strcmp(argv[i], "-16") == 0) pcm_mode = 1;
}
dir = opendir(".");
if (!dir) die("unable to open directory");
while (1) {
f = readdir(dir);
if (!f) break;
//if ((f->d_type & DT_DIR)) continue; // skip directories
//if (!(f->d_type & DT_REG)) continue; // skip special files
if (stat(f->d_name, &s) < 0) continue; // skip if unable to stat
if (S_ISDIR(s.st_mode)) continue; // skip directories
if (!S_ISREG(s.st_mode)) continue; // skip special files
filename = f->d_name;
len = strlen(filename);
if (len < 5) continue;
if (strcasecmp(filename + len - 4, ".wav") != 0) continue;
fp = fopen(filename, "rb");
if (!fp) die("unable to read file %s", filename);
filename2samplename();
printf("converting: %s --> AudioSample%s\n", filename, samplename);
snprintf(buf, sizeof(buf), "AudioSample%s.cpp", samplename);
outc = fopen(buf, "w");
if (outc == NULL) die("unable to write %s", buf);
snprintf(buf, sizeof(buf), "AudioSample%s.h", samplename);
outh = fopen(buf, "w");
if (outh == NULL) die("unable to write %s\n", buf);
fprintf(outh, "%s", title);
fprintf(outc, "%s", title);
fprintf(outc, "#include \"%s\"\n\n", buf);
wav2c(fp, outc, outh);
//wav2c(fp, stdout, stdout);
fclose(outc);
fclose(outh);
fclose(fp);
}
printf("Total data size %d bytes\n", total_length * 4);
return 0;
}
uint8_t read_uint8(FILE *in)
{
int c1;
c1 = fgetc(in);
if (c1 == EOF) die("error, end of data while reading from %s\n", filename);
c1 &= 255;
return c1;
}
int16_t read_int16(FILE *in)
{
int c1, c2;
c1 = fgetc(in);
if (c1 == EOF) die("error, end of data while reading from %s\n", filename);
c2 = fgetc(in);
if (c2 == EOF) die("error, end of data while reading from %s\n", filename);
c1 &= 255;
c2 &= 255;
return (c2 << 8) | c1;
}
uint32_t read_uint32(FILE *in)
{
int c1, c2, c3, c4;
c1 = fgetc(in);
if (c1 == EOF) die("error, end of data while reading from %s\n", filename);
c2 = fgetc(in);
if (c2 == EOF) die("error, end of data while reading from %s\n", filename);
c3 = fgetc(in);
if (c3 == EOF) die("error, end of data while reading from %s\n", filename);
c4 = fgetc(in);
if (c4 == EOF) die("error, end of data while reading from %s\n", filename);
c1 &= 255;
c2 &= 255;
c3 &= 255;
c4 &= 255;
return (c4 << 24) | (c3 << 16) | (c2 << 8) | c1;
}
void die(const char *format, ...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "wav2sketch: ");
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
exit(1);
}

View file

@ -28,7 +28,7 @@
#include "filter_biquad.h"
#include "utility/dspinst.h"
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
void AudioFilterBiquad::update(void)
{
@ -68,6 +68,11 @@ void AudioFilterBiquad::update(void)
bprev = in2;
aprev = pack_16b_16b(
signed_saturate_rshift(sum, 16, 14), out2);
// retaining part of the sum is meant to implement the
// "first order noise shaping" described in this article:
// http://www.earlevel.com/main/2003/02/28/biquads/
// TODO: is logical AND really correct, or maybe it
// should really be signed_saturate_rshift() ???
sum &= 0x3FFF;
bprev = in2;
*data++ = aprev;

View file

@ -46,7 +46,7 @@
// no audible difference.
//#define IMPROVE_EXPONENTIAL_ACCURACY
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
void AudioFilterStateVariable::update_fixed(const int16_t *in,
int16_t *lp, int16_t *bp, int16_t *hp)
@ -120,7 +120,7 @@ void AudioFilterStateVariable::update_variable(const int16_t *in,
n = n << 1;
#else
// exp2 algorithm by Laurent de Soras
// http://www.musicdsp.org/showone.php?id=106
// https://www.musicdsp.org/en/latest/Other/106-fast-exp2-approximation.html
n = (n + 134217728) << 3;
n = multiply_32x32_rshift32_rounded(n, n);
n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;

View file

@ -24,6 +24,8 @@
* THE SOFTWARE.
*/
#if !defined(__IMXRT1052__) && !defined(__IMXRT1062__)
#include <Arduino.h>
#include "input_adc.h"
#include "utility/pdb.h"
@ -211,3 +213,4 @@ void AudioInputAnalog::update(void)
transmit(out_left);
release(out_left);
}
#endif

View file

@ -24,11 +24,13 @@
* THE SOFTWARE.
*/
#include <Arduino.h>
#include "input_i2s.h"
#include "output_i2s.h"
DMAMEM static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES];
static uint32_t i2s_rx_buffer[AUDIO_BLOCK_SAMPLES];
audio_block_t * AudioInputI2S::block_left = NULL;
audio_block_t * AudioInputI2S::block_right = NULL;
uint16_t AudioInputI2S::block_offset = 0;
@ -46,8 +48,8 @@ void AudioInputI2S::begin(void)
// TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputI2S::config_i2s();
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
#if defined(KINETISK)
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
dma.TCD->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 2);
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
@ -59,13 +61,32 @@ void AudioInputI2S::begin(void)
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
#endif
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
update_responsibility = update_setup();
dma.enable();
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
#elif defined(__IMXRT1062__)
CORE_PIN8_CONFIG = 3; //1:RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
dma.TCD->SADDR = (void *)((uint32_t)&I2S1_RDR0 + 2);
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = 0;
dma.TCD->DADDR = i2s_rx_buffer;
dma.TCD->DOFF = 2;
dma.TCD->CITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
#endif
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
}
@ -76,11 +97,11 @@ void AudioInputI2S::isr(void)
int16_t *dest_left, *dest_right;
audio_block_t *left, *right;
//digitalWriteFast(3, HIGH);
#if defined(KINETISK)
#if defined(KINETISK) || defined(__IMXRT1062__)
daddr = (uint32_t)(dma.TCD->DADDR);
#endif
dma.clearInterrupt();
//Serial.println("isr");
if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
@ -102,7 +123,9 @@ void AudioInputI2S::isr(void)
dest_left = &(left->data[offset]);
dest_right = &(right->data[offset]);
AudioInputI2S::block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
do {
//Serial.println(*src);
//n = *src++;
//*dest_left++ = (int16_t)n;
//*dest_right++ = (int16_t)(n >> 16);
@ -111,7 +134,6 @@ void AudioInputI2S::isr(void)
} while (src < end);
}
}
//digitalWriteFast(3, LOW);
}
@ -180,9 +202,9 @@ void AudioInputI2Sslave::begin(void)
//block_right_1st = NULL;
AudioOutputI2Sslave::config_i2s();
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
#if defined(KINETISK)
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
dma.TCD->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 2);
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
@ -194,7 +216,7 @@ void AudioInputI2Sslave::begin(void)
dma.TCD->DLASTSGA = -sizeof(i2s_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
#endif
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
update_responsibility = update_setup();
dma.enable();
@ -202,8 +224,7 @@ void AudioInputI2Sslave::begin(void)
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
dma.attachInterrupt(isr);
#endif
}

View file

@ -0,0 +1,213 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1062__)
#include <Arduino.h>
#include "input_i2s2.h"
#include "output_i2s2.h"
static uint32_t i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES];
audio_block_t * AudioInputI2S2::block_left = NULL;
audio_block_t * AudioInputI2S2::block_right = NULL;
uint16_t AudioInputI2S2::block_offset = 0;
bool AudioInputI2S2::update_responsibility = false;
DMAChannel AudioInputI2S2::dma(false);
void AudioInputI2S2::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
//block_left_1st = NULL;
//block_right_1st = NULL;
// TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputI2S2::config_i2s();
CORE_PIN5_CONFIG = 2; //2:RX_DATA0
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0;
dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0+2);
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = 0;
dma.TCD->DADDR = i2s2_rx_buffer;
dma.TCD->DOFF = 2;
dma.TCD->CITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
dma.TCD->DLASTSGA = -sizeof(i2s2_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX);
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE;
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE;
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
//pinMode(13, OUTPUT);
}
void AudioInputI2S2::isr(void)
{
uint32_t daddr, offset;
const int16_t *src, *end;
int16_t *dest_left, *dest_right;
audio_block_t *left, *right;
//digitalWriteFast(13, HIGH);
daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt();
if (daddr < (uint32_t)i2s2_rx_buffer + sizeof(i2s2_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = (int16_t *)&i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES/2];
end = (int16_t *)&i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES];
if (AudioInputI2S2::update_responsibility) AudioStream::update_all();
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = (int16_t *)&i2s2_rx_buffer[0];
end = (int16_t *)&i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES/2];
}
left = AudioInputI2S2::block_left;
right = AudioInputI2S2::block_right;
if (left != NULL && right != NULL) {
offset = AudioInputI2S2::block_offset;
if (offset <= AUDIO_BLOCK_SAMPLES/2) {
dest_left = &(left->data[offset]);
dest_right = &(right->data[offset]);
AudioInputI2S2::block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
do {
//Serial.println(*src);
//n = *src++;
//*dest_left++ = (int16_t)n;
//*dest_right++ = (int16_t)(n >> 16);
*dest_left++ = *src++;
*dest_right++ = *src++;
} while (src < end);
}
}
//digitalWriteFast(13, LOW);
}
void AudioInputI2S2::update(void)
{
audio_block_t *new_left=NULL, *new_right=NULL, *out_left=NULL, *out_right=NULL;
// allocate 2 new blocks, but if one fails, allocate neither
new_left = allocate();
if (new_left != NULL) {
new_right = allocate();
if (new_right == NULL) {
release(new_left);
new_left = NULL;
}
}
__disable_irq();
if (block_offset >= AUDIO_BLOCK_SAMPLES) {
// the DMA filled 2 blocks, so grab them and get the
// 2 new blocks to the DMA, as quickly as possible
out_left = block_left;
block_left = new_left;
out_right = block_right;
block_right = new_right;
block_offset = 0;
__enable_irq();
// then transmit the DMA's former blocks
transmit(out_left, 0);
release(out_left);
transmit(out_right, 1);
release(out_right);
//Serial.print(".");
} else if (new_left != NULL) {
// the DMA didn't fill blocks, but we allocated blocks
if (block_left == NULL) {
// the DMA doesn't have any blocks to fill, so
// give it the ones we just allocated
block_left = new_left;
block_right = new_right;
block_offset = 0;
__enable_irq();
} else {
// the DMA already has blocks, doesn't need these
__enable_irq();
release(new_left);
release(new_right);
}
} else {
// The DMA didn't fill blocks, and we could not allocate
// memory... the system is likely starving for memory!
// Sadly, there's nothing we can do.
__enable_irq();
}
}
/******************************************************************/
#if 0
void AudioInputI2S2slave::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
//block_left_1st = NULL;
//block_right_1st = NULL;
AudioOutputI2S2slave::config_i2s();
CORE_PIN33_CONFIG = 2; //2:RX_DATA0
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0;
dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0 + 2);
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = 0;
dma.TCD->DADDR = i2s2_rx_buffer;
dma.TCD->DOFF = 2;
dma.TCD->CITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
dma.TCD->DLASTSGA = -sizeof(i2s2_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX);
update_responsibility = update_setup();
dma.enable();
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
dma.attachInterrupt(isr);
}
#endif
#endif

View file

@ -0,0 +1,62 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1052__) || defined(__IMXRT1062__)
#ifndef _input_i2s2_h_
#define _input_i2s2_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"
class AudioInputI2S2 : public AudioStream
{
public:
AudioInputI2S2(void) : AudioStream(0, NULL) { begin(); }
virtual void update(void);
void begin(void);
protected:
AudioInputI2S2(int dummy): AudioStream(0, NULL) {} // to be used only inside AudioInputI2Sslave !!
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_left;
static audio_block_t *block_right;
static uint16_t block_offset;
};
class AudioInputI2S2slave : public AudioInputI2S2
{
public:
AudioInputI2S2slave(void) : AudioInputI2S2(0) { begin(); }
void begin(void);
friend void dma_ch1_isr(void);
};
#endif
#endif //#if defined(__IMXRT1052__) || defined(__IMXRT1062__)

View file

@ -24,6 +24,8 @@
* THE SOFTWARE.
*/
#if !defined(__IMXRT1052__) && !defined(__IMXRT1062__)
#include <Arduino.h>
#include "input_pdm.h"
#include "utility/dspinst.h"
@ -82,12 +84,17 @@ DMAChannel AudioInputPDM::dma(false);
#define MCLK_MULT 1
#define MCLK_DIV 17
#elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000
#define MCLK_MULT 12
#define MCLK_DIV 17
@ -1712,3 +1719,4 @@ for ($n=0; $n < 512; $n += 8) {
print "\n};\n";
print "// max=$max, min=$min\n";
*/
#endif

View file

@ -27,12 +27,14 @@
#include <Arduino.h>
#include "input_tdm.h"
#include "output_tdm.h"
#if defined(KINETISK)
#if defined(KINETISK) || defined(__IMXRT1062__)
#include "utility/imxrt_hw.h"
DMAMEM static uint32_t tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*16];
DMAMEM __attribute__((aligned(32)))
static uint32_t tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*16];
audio_block_t * AudioInputTDM::block_incoming[16] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
bool AudioInputTDM::update_responsibility = false;
DMAChannel AudioInputTDM::dma(false);
@ -44,7 +46,7 @@ void AudioInputTDM::begin(void)
// TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputTDM::config_tdm();
#if defined(KINETISK)
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
dma.TCD->SADDR = &I2S0_RDR0;
dma.TCD->SOFF = 0;
@ -64,6 +66,27 @@ void AudioInputTDM::begin(void)
I2S0_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
dma.attachInterrupt(isr);
#elif defined(__IMXRT1062__)
CORE_PIN8_CONFIG = 3; //RX_DATA0
IOMUXC_SAI1_RX_DATA0_SELECT_INPUT = 2;
dma.TCD->SADDR = &I2S1_RDR0;
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = 4;
dma.TCD->SLAST = 0;
dma.TCD->DADDR = tdm_rx_buffer;
dma.TCD->DOFF = 4;
dma.TCD->CITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_RX);
update_responsibility = update_setup();
dma.enable();
I2S1_RCSR = I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
dma.attachInterrupt(isr);
#endif
}
// TODO: needs optimization...
@ -86,7 +109,6 @@ void AudioInputTDM::isr(void)
const uint32_t *src;
unsigned int i;
//digitalWriteFast(3, HIGH);
daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt();
@ -99,7 +121,10 @@ void AudioInputTDM::isr(void)
// need to remove data from the first half
src = &tdm_rx_buffer[0];
}
if (block_incoming[0] != NULL) {
if (block_incoming[0] != nullptr) {
#if IMXRT_CACHE_ENABLED >=1
arm_dcache_delete((void*)src, sizeof(tdm_rx_buffer) / 2);
#endif
for (i=0; i < 16; i += 2) {
uint32_t *dest1 = (uint32_t *)(block_incoming[i]->data);
uint32_t *dest2 = (uint32_t *)(block_incoming[i+1]->data);
@ -108,7 +133,6 @@ void AudioInputTDM::isr(void)
}
}
if (update_responsibility) update_all();
//digitalWriteFast(3, LOW);
}
@ -121,7 +145,7 @@ void AudioInputTDM::update(void)
// allocate 16 new blocks. If any fails, allocate none
for (i=0; i < 16; i++) {
new_block[i] = allocate();
if (new_block[i] == NULL) {
if (new_block[i] == nullptr) {
for (j=0; j < i; j++) {
release(new_block[j]);
}
@ -133,7 +157,7 @@ void AudioInputTDM::update(void)
memcpy(out_block, block_incoming, sizeof(out_block));
memcpy(block_incoming, new_block, sizeof(block_incoming));
__enable_irq();
if (out_block[0] != NULL) {
if (out_block[0] != nullptr) {
// if we got 1 block, all 16 are filled
for (i=0; i < 16; i++) {
transmit(out_block[i], i);
@ -143,4 +167,4 @@ void AudioInputTDM::update(void)
}
#endif // KINETISK
#endif

View file

@ -0,0 +1,151 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1062__)
#include <Arduino.h>
#include "input_tdm2.h"
#include "output_tdm2.h"
#include "utility/imxrt_hw.h"
DMAMEM __attribute__((aligned(32)))
static uint32_t tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*16];
audio_block_t * AudioInputTDM2::block_incoming[16] = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
bool AudioInputTDM2::update_responsibility = false;
DMAChannel AudioInputTDM2::dma(false);
void AudioInputTDM2::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
// TODO: should we set & clear the I2S_RCSR_SR bit here?
AudioOutputTDM2::config_tdm();
CORE_PIN5_CONFIG = 2; //2:RX_DATA0
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0;
dma.TCD->SADDR = &I2S2_RDR0;
dma.TCD->SOFF = 0;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = 4;
dma.TCD->SLAST = 0;
dma.TCD->DADDR = tdm_rx_buffer;
dma.TCD->DOFF = 4;
dma.TCD->CITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
dma.TCD->BITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX);
update_responsibility = update_setup();
dma.enable();
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE;
dma.attachInterrupt(isr);
}
// TODO: needs optimization...
static void memcpy_tdm_rx(uint32_t *dest1, uint32_t *dest2, const uint32_t *src)
{
uint32_t i, in1, in2;
for (i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
in1 = *src;
in2 = *(src+8);
src += 16;
*dest1++ = (in1 >> 16) | (in2 & 0xFFFF0000);
*dest2++ = (in1 << 16) | (in2 & 0x0000FFFF);
}
}
void AudioInputTDM2::isr(void)
{
uint32_t daddr;
const uint32_t *src;
unsigned int i;
daddr = (uint32_t)(dma.TCD->DADDR);
dma.clearInterrupt();
if (daddr < (uint32_t)tdm_rx_buffer + sizeof(tdm_rx_buffer) / 2) {
// DMA is receiving to the first half of the buffer
// need to remove data from the second half
src = &tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*8];
} else {
// DMA is receiving to the second half of the buffer
// need to remove data from the first half
src = &tdm_rx_buffer[0];
}
if (block_incoming[0] != nullptr) {
#if IMXRT_CACHE_ENABLED >=1
arm_dcache_delete((void*)src, sizeof(tdm_rx_buffer) / 2);
#endif
for (i=0; i < 16; i += 2) {
uint32_t *dest1 = (uint32_t *)(block_incoming[i]->data);
uint32_t *dest2 = (uint32_t *)(block_incoming[i+1]->data);
memcpy_tdm_rx(dest1, dest2, src);
src++;
}
}
if (update_responsibility) update_all();
}
void AudioInputTDM2::update(void)
{
unsigned int i, j;
audio_block_t *new_block[16];
audio_block_t *out_block[16];
// allocate 16 new blocks. If any fails, allocate none
for (i=0; i < 16; i++) {
new_block[i] = allocate();
if (new_block[i] == nullptr) {
for (j=0; j < i; j++) {
release(new_block[j]);
}
memset(new_block, 0, sizeof(new_block));
break;
}
}
__disable_irq();
memcpy(out_block, block_incoming, sizeof(out_block));
memcpy(block_incoming, new_block, sizeof(block_incoming));
__enable_irq();
if (out_block[0] != nullptr) {
// if we got 1 block, all 16 are filled
for (i=0; i < 16; i++) {
transmit(out_block[i], i);
release(out_block[i]);
}
}
}
#endif

View file

@ -0,0 +1,50 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1052__) || defined(__IMXRT1062__)
#ifndef _input_tdm2_h_
#define _input_tdm2_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"
class AudioInputTDM2 : public AudioStream
{
public:
AudioInputTDM2(void) : AudioStream(0, NULL) { begin(); }
virtual void update(void);
void begin(void);
protected:
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_incoming[16];
};
#endif
#endif

View file

@ -1,19 +1,27 @@
Audio KEYWORD2
AudioConnection KEYWORD2
AudioInputI2S KEYWORD2
AudioInputI2S2 KEYWORD2
AudioInputI2SQuad KEYWORD2
AudioInputI2Sslave KEYWORD2
AudioInputTDM KEYWORD2
AudioInputTDM2 KEYWORD2
AudioInputPDM KEYWORD2
AudioInputUSB KEYWORD2
AudioOutputI2S KEYWORD2
AudioOutputI2S2 KEYWORD2
AudioOutputI2SQuad KEYWORD2
AudioOutputI2Sslave KEYWORD2
AudioOutputSPDIF KEYWORD2
AudioOutputSPDIF2 KEYWORD2
AudioOutputSPDIF3 KEYWORD2
AudioOutputPT8211 KEYWORD2
AudioOutputPT8211_2 KEYWORD2
AudioOutputTDM KEYWORD2
AudioOutputTDM2 KEYWORD2
AudioOutputADAT KEYWORD2
AudioOutputPWM KEYWORD2
AudioOutputMQS KEYWORD2
AudioOutputUSB KEYWORD2
AudioControlSGTL5000 KEYWORD2
AudioControlWM8731 KEYWORD2
@ -90,6 +98,8 @@ setLowpass KEYWORD2
setHighpass KEYWORD2
setBandpass KEYWORD2
setNotch KEYWORD2
setLowShelf KEYWORD2
setHighShelf KEYWORD2
muteOutput KEYWORD2
unmuteOutput KEYWORD2
muteInput KEYWORD2

View file

@ -1,5 +1,5 @@
{
"name": "Audio_SdFat",
"name": "Audio",
"frameworks": "Arduino",
"platforms": "Teensy",
"keywords": "sound, audio, FFT, filter, effect",

View file

@ -1,4 +1,4 @@
name=Audio_SdFat
name=Audio
version=1.3
author=Paul Stoffregen
maintainer=Paul Stoffregen
@ -7,3 +7,4 @@ paragraph=A toolkit for building streaming audio projects, featuring Polyphonic
category=Signal Input/Output
url=http://www.pjrc.com/teensy/td_libs_Audio.html
architectures=*

View file

@ -1,5 +1,5 @@
/* Teensyduino Audio Memcpy
* Copyright (c) 2016 Frank Bösing
* Copyright (c) 2016, 2017, 2018, 2019 Frank Bösing
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -27,15 +27,13 @@
* SOFTWARE.
*/
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
#if defined (__ARM_ARCH_7EM__)
#include <AudioStream.h>
.cpu cortex-m4
.syntax unified
.thumb
.text
.align 2
/* void memcpy_tointerleave(short *dst, short *srcL, short *srcR); */
.global memcpy_tointerleaveLR

View file

@ -28,7 +28,7 @@
#include "mixer.h"
#include "utility/dspinst.h"
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
#define MULTI_UNITYGAIN 65536
static void applyGain(int16_t *data, int32_t mult)

View file

@ -32,7 +32,7 @@
class AudioMixer4 : public AudioStream
{
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
public:
AudioMixer4(void) : AudioStream(4, inputQueueArray) {
for (int i=0; i<4; i++) multiplier[i] = 65536;

View file

@ -665,6 +665,54 @@ void AudioOutputADAT::config_ADAT(void)
#endif
}
/*
https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate
*/
void AudioOutputADAT::setI2SFreq(int freq) {
typedef struct {
uint8_t mult;
uint16_t div;
} tmclk;
const int numfreqs = 14;
const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int) 44117.64706 , 48000, 88200, (int) (44117.64706 * 2.0), 96000, 176400,(int) (44117.64706 * 4.0), 192000};
#if (F_PLL==16000000)
const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==168000000)
const tmclk clkArr[numfreqs] = {{32, 2625}, {21, 1250}, {64, 2625}, {21, 625}, {128, 2625}, {42, 625}, {8, 119}, {64, 875}, {84, 625}, {16, 119}, {128, 875}, {168, 625}, {32, 119}, {189, 646} };
#elif (F_PLL==180000000)
const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#elif (F_PLL==256000000)
// TODO: fix these...
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif
for (int f = 0; f < numfreqs; f++) {
if ( freq == samplefreqs[f] ) {
while (I2S0_MCR & I2S_MCR_DUF) ;
I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
return;
}
}
}
#elif defined(KINETISL)
@ -692,46 +740,3 @@ void AudioOutputADAT::update(void)
#endif
/*
https://forum.pjrc.com/threads/38753-Discussion-about-a-simple-way-to-change-the-sample-rate
*/
void AudioOutputADAT::setI2SFreq(int freq) {
typedef struct {
uint8_t mult;
uint16_t div;
} tmclk;
const int numfreqs = 14;
const int samplefreqs[numfreqs] = { 8000, 11025, 16000, 22050, 32000, 44100, (int) 44117.64706 , 48000, 88200, (int) (44117.64706 * 2.0), 96000, 176400,(int) (44117.64706 * 4.0), 192000};
#if (F_PLL==16000000)
const tmclk clkArr[numfreqs] = {{16, 125}, {148, 839}, {32, 125}, {145, 411}, {64, 125}, {151, 214}, {12, 17}, {96, 125}, {151, 107}, {24, 17}, {192, 125}, {127, 45}, {48, 17}, {255, 83} };
#elif (F_PLL==72000000)
const tmclk clkArr[numfreqs] = {{32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {128, 1125}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375}, {249, 397}, {32, 51}, {185, 271} };
#elif (F_PLL==96000000)
const tmclk clkArr[numfreqs] = {{8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {32, 375}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125}, {151, 321}, {8, 17}, {64, 125} };
#elif (F_PLL==120000000)
const tmclk clkArr[numfreqs] = {{32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {128, 1875}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625}, {178, 473}, {32, 85}, {145, 354} };
#elif (F_PLL==144000000)
const tmclk clkArr[numfreqs] = {{16, 1125}, {49, 2500}, {32, 1125}, {49, 1250}, {64, 1125}, {49, 625}, {4, 51}, {32, 375}, {98, 625}, {8, 51}, {64, 375}, {196, 625}, {16, 51}, {128, 375} };
#elif (F_PLL==180000000)
const tmclk clkArr[numfreqs] = {{46, 4043}, {49, 3125}, {73, 3208}, {98, 3125}, {183, 4021}, {196, 3125}, {16, 255}, {128, 1875}, {107, 853}, {32, 255}, {219, 1604}, {214, 853}, {64, 255}, {219, 802} };
#elif (F_PLL==192000000)
const tmclk clkArr[numfreqs] = {{4, 375}, {37, 2517}, {8, 375}, {73, 2483}, {16, 375}, {147, 2500}, {1, 17}, {8, 125}, {147, 1250}, {2, 17}, {16, 125}, {147, 625}, {4, 17}, {32, 125} };
#elif (F_PLL==216000000)
const tmclk clkArr[numfreqs] = {{32, 3375}, {49, 3750}, {64, 3375}, {49, 1875}, {128, 3375}, {98, 1875}, {8, 153}, {64, 1125}, {196, 1875}, {16, 153}, {128, 1125}, {226, 1081}, {32, 153}, {147, 646} };
#elif (F_PLL==240000000)
const tmclk clkArr[numfreqs] = {{16, 1875}, {29, 2466}, {32, 1875}, {89, 3784}, {64, 1875}, {147, 3125}, {4, 85}, {32, 625}, {205, 2179}, {8, 85}, {64, 625}, {89, 473}, {16, 85}, {128, 625} };
#endif
for (int f = 0; f < numfreqs; f++) {
if ( freq == samplefreqs[f] ) {
while (I2S0_MCR & I2S_MCR_DUF) ;
I2S0_MDR = I2S_MDR_FRACT((clkArr[f].mult - 1)) | I2S_MDR_DIVIDE((clkArr[f].div - 1));
return;
}
}
}

View file

@ -35,8 +35,12 @@ audio_block_t * AudioOutputI2S::block_right_2nd = NULL;
uint16_t AudioOutputI2S::block_left_offset = 0;
uint16_t AudioOutputI2S::block_right_offset = 0;
bool AudioOutputI2S::update_responsibility = false;
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
DMAChannel AudioOutputI2S::dma(false);
DMAMEM __attribute__((aligned(32))) static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
#if defined(__IMXRT1062__)
#include "utility/imxrt_hw.h"
#endif
void AudioOutputI2S::begin(void)
{
@ -45,11 +49,11 @@ void AudioOutputI2S::begin(void)
block_left_1st = NULL;
block_right_1st = NULL;
// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
#if defined(KINETISK)
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
@ -61,20 +65,40 @@ void AudioOutputI2S::begin(void)
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
#endif
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
update_responsibility = update_setup();
dma.enable();
I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0 + 2);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif
update_responsibility = update_setup();
dma.attachInterrupt(isr);
}
void AudioOutputI2S::isr(void)
{
#if defined(KINETISK)
#if defined(KINETISK) || defined(__IMXRT1062__)
int16_t *dest;
audio_block_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR;
@ -109,9 +133,10 @@ void AudioOutputI2S::isr(void)
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else {
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
return;
}
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 );
if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S::block_left_offset = offsetL;
} else {
@ -250,7 +275,7 @@ void AudioOutputI2S::update(void)
}
}
#if defined(KINETISK) || defined(KINETISL)
// MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate
//
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
@ -277,12 +302,17 @@ void AudioOutputI2S::update(void)
#define MCLK_MULT 1
#define MCLK_DIV 17
#elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000
#define MCLK_MULT 12
#define MCLK_DIV 17
@ -297,9 +327,12 @@ void AudioOutputI2S::update(void)
#define MCLK_SRC 0 // system clock
#endif
#endif
#endif
void AudioOutputI2S::config_i2s(void)
{
#if defined(KINETISK) || defined(KINETISL)
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
@ -337,14 +370,74 @@ void AudioOutputI2S::config_i2s(void)
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, 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 11, PTC6, I2S0_MCLK
}
#elif defined(__IMXRT1062__)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f
// Select MCLK
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1
& ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));
CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
//I2S1_TCSR = (1<<25); //Reset
I2S1_TCR1 = I2S_TCR1_RFW(1);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF
| I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1));
I2S1_RMR = 0;
//I2S1_RCSR = (1<<25); //Reset
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1));
#endif
}
/******************************************************************/
void AudioOutputI2Sslave::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
//pinMode(2, OUTPUT);
@ -352,8 +445,9 @@ void AudioOutputI2Sslave::begin(void)
block_right_1st = NULL;
AudioOutputI2Sslave::config_i2s();
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
#if defined(KINETISK)
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
@ -365,18 +459,34 @@ void AudioOutputI2Sslave::begin(void)
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
#endif
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
update_responsibility = update_setup();
dma.enable();
I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
//CORE_PIN2_CONFIG = 2; //2:TX_DATA0
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR1 + 2);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
#endif
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
}
void AudioOutputI2Sslave::config_i2s(void)
{
#if defined(KINETISK)
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
@ -416,6 +526,39 @@ void AudioOutputI2Sslave::config_i2s(void)
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, 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 11, PTC6, I2S0_MCLK
#elif defined(__IMXRT1062__)
// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
//Select MCLK
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1
& ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK | ((uint32_t)(1<<20)) ))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0));
CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
// configure transmitter
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size
I2S1_TCR2 = I2S_TCR2_SYNC(1) | I2S_TCR2_BCP;
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
// configure receiver
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(1);
I2S1_RCR2 = I2S_RCR2_SYNC(0) | I2S_TCR2_BCP;
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
#endif
}

View file

@ -0,0 +1,314 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1062__)
#include <Arduino.h>
#include "output_i2s2.h"
#include "memcpy_audio.h"
#include "utility/imxrt_hw.h"
audio_block_t * AudioOutputI2S2::block_left_1st = NULL;
audio_block_t * AudioOutputI2S2::block_right_1st = NULL;
audio_block_t * AudioOutputI2S2::block_left_2nd = NULL;
audio_block_t * AudioOutputI2S2::block_right_2nd = NULL;
uint16_t AudioOutputI2S2::block_left_offset = 0;
uint16_t AudioOutputI2S2::block_right_offset = 0;
bool AudioOutputI2S2::update_responsibility = false;
DMAChannel AudioOutputI2S2::dma(false);
DMAMEM __attribute__((aligned(32))) static uint32_t i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES];
#include "utility/imxrt_hw.h"
void AudioOutputI2S2::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
block_left_1st = NULL;
block_right_1st = NULL;
config_i2s();
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
dma.TCD->SADDR = i2s2_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s2_tx_buffer);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
// I2S2_RCSR |= I2S_RCSR_RE;
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
update_responsibility = update_setup();
dma.attachInterrupt(isr);
dma.enable();
}
void AudioOutputI2S2::isr(void)
{
int16_t *dest;
audio_block_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)i2s2_tx_buffer + sizeof(i2s2_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (int16_t *)&i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
if (AudioOutputI2S2::update_responsibility) AudioStream::update_all();
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = (int16_t *)i2s2_tx_buffer;
}
blockL = AudioOutputI2S2::block_left_1st;
blockR = AudioOutputI2S2::block_right_1st;
offsetL = AudioOutputI2S2::block_left_offset;
offsetR = AudioOutputI2S2::block_right_offset;
if (blockL && blockR) {
memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else if (blockL) {
memcpy_tointerleaveL(dest, blockL->data + offsetL);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
} else if (blockR) {
memcpy_tointerleaveR(dest, blockR->data + offsetR);
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else {
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
}
#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(dest, sizeof(i2s2_tx_buffer) / 2 );
#endif
if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S2::block_left_offset = offsetL;
} else {
AudioOutputI2S2::block_left_offset = 0;
AudioStream::release(blockL);
AudioOutputI2S2::block_left_1st = AudioOutputI2S2::block_left_2nd;
AudioOutputI2S2::block_left_2nd = NULL;
}
if (offsetR < AUDIO_BLOCK_SAMPLES) {
AudioOutputI2S2::block_right_offset = offsetR;
} else {
AudioOutputI2S2::block_right_offset = 0;
AudioStream::release(blockR);
AudioOutputI2S2::block_right_1st = AudioOutputI2S2::block_right_2nd;
AudioOutputI2S2::block_right_2nd = NULL;
}
}
void AudioOutputI2S2::update(void)
{
// null audio device: discard all incoming data
//if (!active) return;
//audio_block_t *block = receiveReadOnly();
//if (block) release(block);
audio_block_t *block;
block = receiveReadOnly(0); // input 0 = left channel
if (block) {
__disable_irq();
if (block_left_1st == NULL) {
block_left_1st = block;
block_left_offset = 0;
__enable_irq();
} else if (block_left_2nd == NULL) {
block_left_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = block;
block_left_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // input 1 = right channel
if (block) {
__disable_irq();
if (block_right_1st == NULL) {
block_right_1st = block;
block_right_offset = 0;
__enable_irq();
} else if (block_right_2nd == NULL) {
block_right_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = block;
block_right_offset = 0;
__enable_irq();
release(tmp);
}
}
}
void AudioOutputI2S2::config_i2s(void)
{
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
// clear SAI2_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
// if either transmitter or receiver is enabled, do nothing
if (I2S2_TCSR & I2S_TCSR_TE) return;
if (I2S2_RCSR & I2S_RCSR_RE) return;
CORE_PIN33_CONFIG = 2; //2:MCLK
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
int rsync = 1;
int tsync = 0;
I2S2_TMR = 0;
//I2S2_TCSR = (1<<25); //Reset
I2S2_TCR1 = I2S_TCR1_RFW(1);
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
I2S2_TCR3 = I2S_TCR3_TCE;
I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S2_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1));
I2S2_RMR = 0;
//I2S2_RCSR = (1<<25); //Reset
I2S2_RCR1 = I2S_RCR1_RFW(1);
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
I2S2_RCR3 = I2S_RCR3_RCE;
I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S2_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1));
}
/******************************************************************/
#if 0
void AudioOutputI2S2slave::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
//pinMode(2, OUTPUT);
block_left_1st = NULL;
block_right_1st = NULL;
AudioOutputI2S2slave::config_i2s();
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
//CORE_PIN33_CONFIG = 2; //2:RX_DATA0
dma.TCD->SADDR = i2s2_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s2_tx_buffer);
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
}
void AudioOutputI2S2slave::config_i2s(void)
{
if (I2S2_TCSR & I2S_TCSR_TE) return;
if (I2S2_TCSR & I2S_RCSR_RE) return;
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
/*
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
*/
// IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK | ((uint32_t)(1<<19)) ))
// /*| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR*/ | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0); //Select MCLK
CORE_PIN5_CONFIG = 2; //2:MCLK
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
int rsync = 1;
int tsync = 0;
// configure transmitter
I2S2_TMR = 0;
I2S2_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP;
I2S2_TCR3 = I2S_TCR3_TCE;
I2S2_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP;
I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
// configure receiver
I2S2_TMR = 0;
I2S2_TCR1 = I2S_RCR1_RFW(1);
I2S2_TCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP;
I2S2_TCR3 = I2S_RCR3_RCE;
I2S2_TCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
I2S2_TCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
}
#endif
#endif //defined(__IMXRT1062__)

View file

@ -0,0 +1,73 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1052__) || defined(__IMXRT1062__)
#ifndef output_i2s2_h_
#define output_i2s2_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"
class AudioOutputI2S2 : public AudioStream
{
public:
AudioOutputI2S2(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
friend class AudioInputI2S2;
protected:
AudioOutputI2S2(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
static void config_i2s(void);
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_left_2nd;
static audio_block_t *block_right_2nd;
static uint16_t block_left_offset;
static uint16_t block_right_offset;
audio_block_t *inputQueueArray[2];
};
class AudioOutputI2S2slave : public AudioOutputI2S2
{
public:
AudioOutputI2S2slave(void) : AudioOutputI2S2(0) { begin(); } ;
void begin(void);
friend class AudioInputI2S2slave;
friend void dma_ch0_isr(void);
protected:
static void config_i2s(void);
};
#endif
#endif //defined(__IMXRT1062__)

View file

@ -272,12 +272,17 @@ void AudioOutputI2SQuad::update(void)
#define MCLK_MULT 1
#define MCLK_DIV 17
#elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000
#define MCLK_MULT 12
#define MCLK_DIV 17

View file

@ -0,0 +1,234 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
// Frank B
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include <Arduino.h>
#include "output_mqs.h"
#include "memcpy_audio.h"
#include "utility/imxrt_hw.h"
audio_block_t * AudioOutputMQS::block_left_1st = NULL;
audio_block_t * AudioOutputMQS::block_right_1st = NULL;
audio_block_t * AudioOutputMQS::block_left_2nd = NULL;
audio_block_t * AudioOutputMQS::block_right_2nd = NULL;
uint16_t AudioOutputMQS::block_left_offset = 0;
uint16_t AudioOutputMQS::block_right_offset = 0;
bool AudioOutputMQS::update_responsibility = false;
DMAChannel AudioOutputMQS::dma(false);
DMAMEM __attribute__((aligned(32)))
static uint32_t I2S3_tx_buffer[AUDIO_BLOCK_SAMPLES];
void AudioOutputMQS::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
block_left_1st = NULL;
block_right_1st = NULL;
config_i2s();
CORE_PIN10_CONFIG = 2;//B0_00 MQS_RIGHT
CORE_PIN12_CONFIG = 2;//B0_01 MQS_LEFT
dma.TCD->SADDR = I2S3_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(I2S3_tx_buffer);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(I2S3_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(I2S3_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S3_TDR0 + 0);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI3_TX);
I2S3_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
update_responsibility = update_setup();
dma.attachInterrupt(isr);
dma.enable();
}
void AudioOutputMQS::isr(void)
{
int16_t *dest;
audio_block_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)I2S3_tx_buffer + sizeof(I2S3_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (int16_t *)&I2S3_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
if (AudioOutputMQS::update_responsibility) AudioStream::update_all();
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = (int16_t *)I2S3_tx_buffer;
}
blockL = AudioOutputMQS::block_left_1st;
blockR = AudioOutputMQS::block_right_1st;
offsetL = AudioOutputMQS::block_left_offset;
offsetR = AudioOutputMQS::block_right_offset;
if (blockL && blockR) {
memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else if (blockL) {
memcpy_tointerleaveL(dest, blockL->data + offsetL);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
} else if (blockR) {
memcpy_tointerleaveR(dest, blockR->data + offsetR);
offsetR += AUDIO_BLOCK_SAMPLES / 2;
} else {
memset(dest,0, sizeof(I2S3_tx_buffer) / 2);
}
#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(dest, sizeof(I2S3_tx_buffer) / 2 );
#endif
if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputMQS::block_left_offset = offsetL;
} else {
AudioOutputMQS::block_left_offset = 0;
AudioStream::release(blockL);
AudioOutputMQS::block_left_1st = AudioOutputMQS::block_left_2nd;
AudioOutputMQS::block_left_2nd = NULL;
}
if (offsetR < AUDIO_BLOCK_SAMPLES) {
AudioOutputMQS::block_right_offset = offsetR;
} else {
AudioOutputMQS::block_right_offset = 0;
AudioStream::release(blockR);
AudioOutputMQS::block_right_1st = AudioOutputMQS::block_right_2nd;
AudioOutputMQS::block_right_2nd = NULL;
}
}
void AudioOutputMQS::update(void)
{
// null audio device: discard all incoming data
//if (!active) return;
//audio_block_t *block = receiveReadOnly();
//if (block) release(block);
//digitalWriteFast(13, LOW);
audio_block_t *block;
block = receiveReadOnly(0); // input 0 = left channel
if (block) {
__disable_irq();
if (block_left_1st == NULL) {
block_left_1st = block;
block_left_offset = 0;
__enable_irq();
} else if (block_left_2nd == NULL) {
block_left_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = block;
block_left_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // input 1 = right channel
if (block) {
__disable_irq();
if (block_right_1st == NULL) {
block_right_1st = block;
block_right_offset = 0;
__enable_irq();
} else if (block_right_2nd == NULL) {
block_right_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = block;
block_right_offset = 0;
__enable_irq();
release(tmp);
}
}
}
void AudioOutputMQS::config_i2s(void)
{
CCM_CCGR5 |= CCM_CCGR5_SAI3(CCM_CCGR_ON);
CCM_CCGR0 |= CCM_CCGR0_MQS_HMCLK(CCM_CCGR_ON);
//PLL:
//TODO: Check if frequencies are correct!
int fs = AUDIO_SAMPLE_RATE_EXACT;
int oversample = 64*8;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * oversample * n1);
double C = ((double)fs * oversample * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI3_CLK_SEL_MASK))
| CCM_CSCMR1_SAI3_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI3_CLK_PRED_MASK | CCM_CS1CDR_SAI3_CLK_PODF_MASK))
| CCM_CS1CDR_SAI3_CLK_PRED(n1-1)
| CCM_CS1CDR_SAI3_CLK_PODF(n2-1);
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI3_MCLK3_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI3_MCLK_DIR | IOMUXC_GPR_GPR1_SAI3_MCLK3_SEL(0)); //Select MCLK
IOMUXC_GPR_GPR2 = (IOMUXC_GPR_GPR2 & ~(IOMUXC_GPR_GPR2_MQS_OVERSAMPLE | IOMUXC_GPR_GPR2_MQS_CLK_DIV_MASK))
| IOMUXC_GPR_GPR2_MQS_EN | IOMUXC_GPR_GPR2_MQS_OVERSAMPLE | IOMUXC_GPR_GPR2_MQS_CLK_DIV(0);
if (I2S3_TCSR & I2S_TCSR_TE) return;
I2S3_TMR = 0;
// I2S3_TCSR = (1<<25); //Reset
I2S3_TCR1 = I2S_TCR1_RFW(1);
I2S3_TCR2 = I2S_TCR2_SYNC(0) /*| I2S_TCR2_BCP*/ // sync=0; tx is async;
| (I2S_TCR2_BCD | I2S_TCR2_DIV((7)) | I2S_TCR2_MSEL(1));
I2S3_TCR3 = I2S_TCR3_TCE;
I2S3_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((16-1)) | I2S_TCR4_MF | I2S_TCR4_FSD /*| I2S_TCR4_FSE*/ /* | I2S_TCR4_FSP */;
I2S3_TCR5 = I2S_TCR5_WNW((16-1)) | I2S_TCR5_W0W((16-1)) | I2S_TCR5_FBT((16-1));
}
#endif //defined(__IMXRT1062__)

View file

@ -0,0 +1,58 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1052__) || defined(__IMXRT1062__)
#ifndef output_mqs_h_
#define output_mqs_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"
class AudioOutputMQS : public AudioStream
{
public:
AudioOutputMQS(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
friend class AudioInputI2S2;
protected:
static void config_i2s(void);
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_left_2nd;
static audio_block_t *block_right_2nd;
static uint16_t block_left_offset;
static uint16_t block_right_offset;
audio_block_t *inputQueueArray[2];
};
#endif
#endif //defined(__IMXRT1062__)

View file

@ -26,9 +26,11 @@
//Adapted to PT8211, Frank Bösing, Ben-Rheinland
#include <Arduino.h>
#include "output_pt8211.h"
#include "memcpy_audio.h"
#include "utility/imxrt_hw.h"
audio_block_t * AudioOutputPT8211::block_left_1st = NULL;
audio_block_t * AudioOutputPT8211::block_right_1st = NULL;
@ -38,9 +40,9 @@ uint16_t AudioOutputPT8211::block_left_offset = 0;
uint16_t AudioOutputPT8211::block_right_offset = 0;
bool AudioOutputPT8211::update_responsibility = false;
#if defined(AUDIO_PT8211_OVERSAMPLING)
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4];
static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4];
#else
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
#endif
DMAChannel AudioOutputPT8211::dma(false);
@ -53,9 +55,9 @@ void AudioOutputPT8211::begin(void)
// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_i2s();
#if defined(KINETISK)
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
#if defined(KINETISK)
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
@ -67,13 +69,42 @@ void AudioOutputPT8211::begin(void)
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
#endif
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
update_responsibility = update_setup();
dma.enable();
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
dma.attachInterrupt(isr);
dma.enable();
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
return;
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1052__)
CORE_PIN6_CONFIG = 3; //1:TX_DATA0
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
#endif
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S1_TDR0);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
I2S1_RCSR |= I2S_RCSR_RE;
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
update_responsibility = update_setup();
dma.attachInterrupt(isr);
dma.enable();
return;
#endif
}
void AudioOutputPT8211::isr(void)
@ -103,7 +134,7 @@ void AudioOutputPT8211::isr(void)
blockR = AudioOutputPT8211::block_right_1st;
offsetL = AudioOutputPT8211::block_left_offset;
offsetR = AudioOutputPT8211::block_right_offset;
#if defined(AUDIO_PT8211_OVERSAMPLING)
static int32_t oldL = 0;
static int32_t oldR = 0;
@ -132,12 +163,12 @@ void AudioOutputPT8211::isr(void)
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
int32_t valL = blockL->data[offsetL];
int32_t valR = blockR->data[offsetR];
int32_t combL[3] = {0};
static int32_t combLOld[2] = {0};
int32_t combR[3] = {0};
static int32_t combROld[2] = {0};
combL[0] = valL - oldL;
combR[0] = valR - oldR;
combL[1] = combL[0] - combLOld[0];
@ -184,7 +215,7 @@ void AudioOutputPT8211::isr(void)
offsetL += AUDIO_BLOCK_SAMPLES / 2;
offsetR += AUDIO_BLOCK_SAMPLES / 2;
#endif //defined(AUDIO_PT8211_OVERSAMPLING)
} else if (blockL) {
#if defined(AUDIO_PT8211_OVERSAMPLING)
#if defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
@ -208,14 +239,14 @@ void AudioOutputPT8211::isr(void)
int32_t combL[3] = {0};
static int32_t combLOld[2] = {0};
combL[0] = valL - oldL;
combL[1] = combL[0] - combLOld[0];
combL[2] = combL[1] - combLOld[1];
// combL[2] now holds input val
combLOld[0] = combL[0];
combLOld[1] = combL[1];
for (int j = 0; j < 4; j++) {
int32_t integrateL[3];
static int32_t integrateLOld[3] = {0};
@ -228,7 +259,7 @@ void AudioOutputPT8211::isr(void)
integrateLOld[1] = integrateL[1];
integrateLOld[2] = integrateL[2];
}
// fill right channel with zeros:
*(dest+1) = 0;
*(dest+3) = 0;
@ -240,7 +271,7 @@ void AudioOutputPT8211::isr(void)
#else
#error no interpolation method defined for oversampling.
#endif //defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
#else
#else
memcpy_tointerleaveL(dest, blockL->data + offsetL);
offsetL += (AUDIO_BLOCK_SAMPLES / 2);
#endif //defined(AUDIO_PT8211_OVERSAMPLING)
@ -267,14 +298,14 @@ void AudioOutputPT8211::isr(void)
int32_t combR[3] = {0};
static int32_t combROld[2] = {0};
combR[0] = valR - oldR;
combR[1] = combR[0] - combROld[0];
combR[2] = combR[1] - combROld[1];
// combR[2] now holds input val
combROld[0] = combR[0];
combROld[1] = combR[1];
for (int j = 0; j < 4; j++) {
int32_t integrateR[3];
static int32_t integrateROld[3] = {0};
@ -287,7 +318,7 @@ void AudioOutputPT8211::isr(void)
integrateROld[1] = integrateR[1];
integrateROld[2] = integrateR[2];
}
// fill left channel with zeros:
*(dest+0) = 0;
*(dest+2) = 0;
@ -307,7 +338,7 @@ void AudioOutputPT8211::isr(void)
#if defined(AUDIO_PT8211_OVERSAMPLING)
memset(dest,0,AUDIO_BLOCK_SAMPLES*8);
#else
memset(dest,0,AUDIO_BLOCK_SAMPLES*2);
memset(dest,0,AUDIO_BLOCK_SAMPLES*2);
#endif
return;
}
@ -376,7 +407,7 @@ void AudioOutputPT8211::update(void)
}
}
#if defined(KINETISK)
// MCLK needs to be 48e6 / 1088 * 256 = 11.29411765 MHz -> 44.117647 kHz sample rate
//
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
@ -403,12 +434,17 @@ void AudioOutputPT8211::update(void)
#define MCLK_MULT 1
#define MCLK_DIV 17
#elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000
#define MCLK_MULT 12
#define MCLK_DIV 17
@ -423,9 +459,11 @@ void AudioOutputPT8211::update(void)
#define MCLK_SRC 0 // system clock
#endif
#endif
#endif
void AudioOutputPT8211::config_i2s(void)
{
#if defined(KINETISK)
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
@ -455,7 +493,65 @@ void AudioOutputPT8211::config_i2s(void)
// configure pin mux for 3 clock signals
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
#if 0
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
//CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
#elif ( defined(__IMXRT1052__) || defined(__IMXRT1062__) )
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK
if (I2S1_TCSR & I2S_TCSR_TE) return;
// CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
// CORE_PIN6_CONFIG = 3; //1:TX_DATA0
// CORE_PIN7_CONFIG = 3; //1:RX_DATA0
int rsync = 0;
int tsync = 1;
#if defined(AUDIO_PT8211_OVERSAMPLING)
int div = 0;
#else
int div = 3;
#endif
// configure transmitter
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(0);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(div);
I2S1_TCR3 = I2S_TCR3_TCE;
// I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD; //TDA1543
I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF /*| I2S_TCR4_FSE*/ | I2S_TCR4_FSP | I2S_TCR4_FSD; //PT8211
I2S1_TCR5 = I2S_TCR5_WNW(15) | I2S_TCR5_W0W(15) | I2S_TCR5_FBT(15);
I2S1_RMR = 0;
//I2S1_RCSR = (1<<25); //Reset
I2S1_RCR1 = I2S_RCR1_RFW(0);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP | I2S_RCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(div);
I2S1_RCR3 = I2S_RCR3_RCE;
// I2S1_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD; //TDA1543
I2S1_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(15) | I2S_RCR4_MF /*| I2S_RCR4_FSE*/ | I2S_RCR4_FSP | I2S_RCR4_FSD; //PT8211
I2S1_RCR5 = I2S_RCR5_WNW(15) | I2S_RCR5_W0W(15) | I2S_RCR5_FBT(15);
#endif
}

View file

@ -45,9 +45,7 @@ public:
AudioOutputPT8211(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
//friend class AudioInputI2S;
protected:
//AudioOutputI2S(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
static void config_i2s(void);
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;

View file

@ -0,0 +1,427 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2016, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
//Adapted to PT8211, Frank Bösing, Ben-Rheinland
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include <Arduino.h>
#include "output_pt8211_2.h"
#include "memcpy_audio.h"
#include "utility/imxrt_hw.h"
audio_block_t * AudioOutputPT8211_2::block_left_1st = NULL;
audio_block_t * AudioOutputPT8211_2::block_right_1st = NULL;
audio_block_t * AudioOutputPT8211_2::block_left_2nd = NULL;
audio_block_t * AudioOutputPT8211_2::block_right_2nd = NULL;
uint16_t AudioOutputPT8211_2::block_left_offset = 0;
uint16_t AudioOutputPT8211_2::block_right_offset = 0;
bool AudioOutputPT8211_2::update_responsibility = false;
#if defined(AUDIO_PT8211_OVERSAMPLING)
static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4];
#else
static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
#endif
DMAChannel AudioOutputPT8211_2::dma(false);
PROGMEM
void AudioOutputPT8211_2::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
block_left_1st = NULL;
block_right_1st = NULL;
// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_i2s();
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
dma.TCD->SADDR = i2s_tx_buffer;
dma.TCD->SOFF = 2;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
dma.TCD->NBYTES_MLNO = 2;
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0);
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
update_responsibility = update_setup();
dma.attachInterrupt(isr);
dma.enable();
}
void AudioOutputPT8211_2::isr(void)
{
int16_t *dest;
audio_block_t *blockL, *blockR;
uint32_t saddr, offsetL, offsetR;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
#if defined(AUDIO_PT8211_OVERSAMPLING)
dest = (int16_t *)&i2s_tx_buffer[(AUDIO_BLOCK_SAMPLES/2)*4];
#else
dest = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
#endif
if (AudioOutputPT8211_2::update_responsibility) AudioStream::update_all();
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = (int16_t *)i2s_tx_buffer;
}
blockL = AudioOutputPT8211_2::block_left_1st;
blockR = AudioOutputPT8211_2::block_right_1st;
offsetL = AudioOutputPT8211_2::block_left_offset;
offsetR = AudioOutputPT8211_2::block_right_offset;
#if defined(AUDIO_PT8211_OVERSAMPLING)
static int32_t oldL = 0;
static int32_t oldR = 0;
#endif
if (blockL && blockR) {
#if defined(AUDIO_PT8211_OVERSAMPLING)
#if defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
int32_t valL = blockL->data[offsetL];
int32_t valR = blockR->data[offsetR];
int32_t nL = (oldL+valL) >> 1;
int32_t nR = (oldR+valR) >> 1;
*(dest+0) = (oldL+nL) >> 1;
*(dest+1) = (oldR+nR) >> 1;
*(dest+2) = nL;
*(dest+3) = nR;
*(dest+4) = (nL+valL) >> 1;
*(dest+5) = (nR+valR) >> 1;
*(dest+6) = valL;
*(dest+7) = valR;
dest+=8;
oldL = valL;
oldR = valR;
}
#elif defined(AUDIO_PT8211_INTERPOLATION_CIC)
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
int32_t valL = blockL->data[offsetL];
int32_t valR = blockR->data[offsetR];
int32_t combL[3] = {0};
static int32_t combLOld[2] = {0};
int32_t combR[3] = {0};
static int32_t combROld[2] = {0};
combL[0] = valL - oldL;
combR[0] = valR - oldR;
combL[1] = combL[0] - combLOld[0];
combR[1] = combR[0] - combROld[0];
combL[2] = combL[1] - combLOld[1];
combR[2] = combR[1] - combROld[1];
// combL[2] now holds input val
// combR[2] now holds input val
oldL = valL;
oldR = valR;
combLOld[0] = combL[0];
combROld[0] = combR[0];
combLOld[1] = combL[1];
combROld[1] = combR[1];
for (int j = 0; j < 4; j++) {
int32_t integrateL[3];
int32_t integrateR[3];
static int32_t integrateLOld[3] = {0};
static int32_t integrateROld[3] = {0};
integrateL[0] = ( (j==0) ? (combL[2]) : (0) ) + integrateLOld[0];
integrateR[0] = ( (j==0) ? (combR[2]) : (0) ) + integrateROld[0];
integrateL[1] = integrateL[0] + integrateLOld[1];
integrateR[1] = integrateR[0] + integrateROld[1];
integrateL[2] = integrateL[1] + integrateLOld[2];
integrateR[2] = integrateR[1] + integrateROld[2];
// integrateL[2] now holds j'th upsampled value
// integrateR[2] now holds j'th upsampled value
*(dest+j*2) = integrateL[2] >> 4;
*(dest+j*2+1) = integrateR[2] >> 4;
integrateLOld[0] = integrateL[0];
integrateROld[0] = integrateR[0];
integrateLOld[1] = integrateL[1];
integrateROld[1] = integrateR[1];
integrateLOld[2] = integrateL[2];
integrateROld[2] = integrateR[2];
}
dest+=8;
}
#else
#error no interpolation method defined for oversampling.
#endif //defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
#else
memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
offsetL += AUDIO_BLOCK_SAMPLES / 2;
offsetR += AUDIO_BLOCK_SAMPLES / 2;
#endif //defined(AUDIO_PT8211_OVERSAMPLING)
} else if (blockL) {
#if defined(AUDIO_PT8211_OVERSAMPLING)
#if defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++) {
int32_t val = blockL->data[offsetL];
int32_t n = (oldL+val) >> 1;
*(dest+0) = (oldL+n) >> 1;
*(dest+1) = 0;
*(dest+2) = n;
*(dest+3) = 0;
*(dest+4) = (n+val) >> 1;
*(dest+5) = 0;
*(dest+6) = val;
*(dest+7) = 0;
dest+=8;
oldL = val;
}
#elif defined(AUDIO_PT8211_INTERPOLATION_CIC)
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
int32_t valL = blockL->data[offsetL];
int32_t combL[3] = {0};
static int32_t combLOld[2] = {0};
combL[0] = valL - oldL;
combL[1] = combL[0] - combLOld[0];
combL[2] = combL[1] - combLOld[1];
// combL[2] now holds input val
combLOld[0] = combL[0];
combLOld[1] = combL[1];
for (int j = 0; j < 4; j++) {
int32_t integrateL[3];
static int32_t integrateLOld[3] = {0};
integrateL[0] = ( (j==0) ? (combL[2]) : (0) ) + integrateLOld[0];
integrateL[1] = integrateL[0] + integrateLOld[1];
integrateL[2] = integrateL[1] + integrateLOld[2];
// integrateL[2] now holds j'th upsampled value
*(dest+j*2) = integrateL[2] >> 4;
integrateLOld[0] = integrateL[0];
integrateLOld[1] = integrateL[1];
integrateLOld[2] = integrateL[2];
}
// fill right channel with zeros:
*(dest+1) = 0;
*(dest+3) = 0;
*(dest+5) = 0;
*(dest+7) = 0;
dest+=8;
oldL = valL;
}
#else
#error no interpolation method defined for oversampling.
#endif //defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
#else
memcpy_tointerleaveL(dest, blockL->data + offsetL);
offsetL += (AUDIO_BLOCK_SAMPLES / 2);
#endif //defined(AUDIO_PT8211_OVERSAMPLING)
} else if (blockR) {
#if defined(AUDIO_PT8211_OVERSAMPLING)
#if defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetR++) {
int32_t val = blockR->data[offsetR];
int32_t n = (oldR+val) >> 1;
*(dest+0) = 0;
*(dest+1) = ((oldR+n) >> 1);
*(dest+2) = 0;
*(dest+3) = n;
*(dest+4) = 0;
*(dest+5) = ((n+val) >> 1);
*(dest+6) = 0;
*(dest+7) = val;
dest+=8;
oldR = val;
}
#elif defined(AUDIO_PT8211_INTERPOLATION_CIC)
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
int32_t valR = blockR->data[offsetR];
int32_t combR[3] = {0};
static int32_t combROld[2] = {0};
combR[0] = valR - oldR;
combR[1] = combR[0] - combROld[0];
combR[2] = combR[1] - combROld[1];
// combR[2] now holds input val
combROld[0] = combR[0];
combROld[1] = combR[1];
for (int j = 0; j < 4; j++) {
int32_t integrateR[3];
static int32_t integrateROld[3] = {0};
integrateR[0] = ( (j==0) ? (combR[2]) : (0) ) + integrateROld[0];
integrateR[1] = integrateR[0] + integrateROld[1];
integrateR[2] = integrateR[1] + integrateROld[2];
// integrateR[2] now holds j'th upsampled value
*(dest+j*2+1) = integrateR[2] >> 4;
integrateROld[0] = integrateR[0];
integrateROld[1] = integrateR[1];
integrateROld[2] = integrateR[2];
}
// fill left channel with zeros:
*(dest+0) = 0;
*(dest+2) = 0;
*(dest+4) = 0;
*(dest+6) = 0;
dest+=8;
oldR = valR;
}
#else
#error no interpolation method defined for oversampling.
#endif //defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
#else
memcpy_tointerleaveR(dest, blockR->data + offsetR);
offsetR += AUDIO_BLOCK_SAMPLES / 2;
#endif //defined(AUDIO_PT8211_OVERSAMPLING)
} else {
#if defined(AUDIO_PT8211_OVERSAMPLING)
memset(dest,0,AUDIO_BLOCK_SAMPLES*8);
#else
memset(dest,0,AUDIO_BLOCK_SAMPLES*2);
#endif
return;
}
if (offsetL < AUDIO_BLOCK_SAMPLES) {
AudioOutputPT8211_2::block_left_offset = offsetL;
} else {
AudioOutputPT8211_2::block_left_offset = 0;
AudioStream::release(blockL);
AudioOutputPT8211_2::block_left_1st = AudioOutputPT8211_2::block_left_2nd;
AudioOutputPT8211_2::block_left_2nd = NULL;
}
if (offsetR < AUDIO_BLOCK_SAMPLES) {
AudioOutputPT8211_2::block_right_offset = offsetR;
} else {
AudioOutputPT8211_2::block_right_offset = 0;
AudioStream::release(blockR);
AudioOutputPT8211_2::block_right_1st = AudioOutputPT8211_2::block_right_2nd;
AudioOutputPT8211_2::block_right_2nd = NULL;
}
}
void AudioOutputPT8211_2::update(void)
{
audio_block_t *block;
block = receiveReadOnly(0); // input 0 = left channel
if (block) {
__disable_irq();
if (block_left_1st == NULL) {
block_left_1st = block;
block_left_offset = 0;
__enable_irq();
} else if (block_left_2nd == NULL) {
block_left_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = block;
block_left_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // input 1 = right channel
if (block) {
__disable_irq();
if (block_right_1st == NULL) {
block_right_1st = block;
block_right_offset = 0;
__enable_irq();
} else if (block_right_2nd == NULL) {
block_right_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = block;
block_right_offset = 0;
__enable_irq();
release(tmp);
}
}
}
PROGMEM
void AudioOutputPT8211_2::config_i2s(void)
{
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
if (I2S2_TCSR & I2S_TCSR_TE) return;
//CORE_PIN5_CONFIG = 2; //2:MCLK
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
#if defined(AUDIO_PT8211_OVERSAMPLING)
int div = 0;
#else
int div = 3;
#endif
// configure transmitter
I2S2_TMR = 0;
I2S2_TCR1 = I2S_TCR1_RFW(0);
I2S2_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(div);
I2S2_TCR3 = I2S_TCR3_TCE;
// I2S2_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD; //TDA1543
I2S2_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF /*| I2S_TCR4_FSE*/ | I2S_TCR4_FSP | I2S_TCR4_FSD; //PT8211
I2S2_TCR5 = I2S_TCR5_WNW(15) | I2S_TCR5_W0W(15) | I2S_TCR5_FBT(15);
}
#endif

View file

@ -0,0 +1,67 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2016, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
//Frank Bösing, Ben-Rheinland
#ifndef output_pt8211_2_h_
#define output_pt8211_2_h_
//uncomment to enable oversampling:
#define AUDIO_PT8211_OVERSAMPLING
//uncomment ONE of these to define interpolation type for oversampling:
// #define AUDIO_PT8211_INTERPOLATION_LINEAR
#define AUDIO_PT8211_INTERPOLATION_CIC
#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"
class AudioOutputPT8211_2 : public AudioStream
{
public:
AudioOutputPT8211_2(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
protected:
static void config_i2s(void);
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void)
#if defined(AUDIO_PT8211_OVERSAMPLING)
__attribute__((optimize("unroll-loops")))
#endif
;
private:
static audio_block_t *block_left_2nd;
static audio_block_t *block_right_2nd;
static uint16_t block_left_offset;
static uint16_t block_right_offset;
audio_block_t *inputQueueArray[2];
};
#endif

View file

@ -25,15 +25,7 @@
#include <Arduino.h>
#include "output_spdif.h"
#if defined(KINETISK)
#define PREAMBLE_B (0xE8) //11101000
#define PREAMBLE_M (0xE2) //11100010
#define PREAMBLE_W (0xE4) //11100100
#define VUCP_VALID ((0xCC) << 24)
#define VUCP_INVALID ((0xD4) << 24)// To mute PCM, set VUCP = invalid.
#include "utility/imxrt_hw.h"
audio_block_t * AudioOutputSPDIF::block_left_1st = NULL;
audio_block_t * AudioOutputSPDIF::block_right_1st = NULL;
@ -42,49 +34,26 @@ audio_block_t * AudioOutputSPDIF::block_right_2nd = NULL;
uint16_t AudioOutputSPDIF::block_left_offset = 0;
uint16_t AudioOutputSPDIF::block_right_offset = 0;
bool AudioOutputSPDIF::update_responsibility = false;
DMAChannel AudioOutputSPDIF::dma(false);
extern uint16_t spdif_bmclookup[256];
DMAMEM __attribute__((aligned(32)))
static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
#if defined(KINETISK) || defined(__IMXRT1052__) || defined(__IMXRT1062__)
#define PREAMBLE_B (0xE8) //11101000
#define PREAMBLE_M (0xE2) //11100010
#define PREAMBLE_W (0xE4) //11100100
#define VUCP_VALID ((0xCC) << 24)
#define VUCP_INVALID ((0xD4) << 24)// To mute PCM, set VUCP = invalid.
uint32_t AudioOutputSPDIF::vucp = VUCP_VALID;
DMAMEM static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
DMAChannel AudioOutputSPDIF::dma(false);
static const
uint16_t bmclookup[256] = { //biphase mark encoded values (least significant bit first)
0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
};
#endif
PROGMEM
void AudioOutputSPDIF::begin(void)
{
@ -95,6 +64,7 @@ void AudioOutputSPDIF::begin(void)
// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_SPDIF();
#if defined(KINETISK)
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
@ -116,7 +86,35 @@ void AudioOutputSPDIF::begin(void)
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
dma.attachInterrupt(isr);
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
#if defined(__IMXRT1052__)
CORE_PIN6_CONFIG = 3; //1:TX_DATA0
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
#endif
const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
dma.TCD->SADDR = SPDIF_tx_buffer;
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = nbytes_mlno;
dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
dma.TCD->DADDR = &I2S1_TDR0;
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
update_responsibility = update_setup();
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE;
I2S1_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
dma.attachInterrupt(isr);
#endif
}
/*
@ -165,8 +163,8 @@ void AudioOutputSPDIF::isr(void)
sample = *src++;
//Subframe Channel 1
hi = bmclookup[(uint8_t)(sample >> 8)];
lo = bmclookup[(uint8_t) sample];
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
lo = spdif_bmclookup[(uint8_t) sample];
lo ^= (~((int16_t)hi) >> 16);
// 16 Bit sample:
*(dest+1) = ((uint32_t)lo << 16) | hi;
@ -217,8 +215,8 @@ void AudioOutputSPDIF::isr(void)
sample = *src++;
//Subframe Channel 2
hi = bmclookup[(uint8_t)(sample >> 8)];
lo = bmclookup[(uint8_t)sample];
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
lo = spdif_bmclookup[(uint8_t)sample];
lo ^= (~((int16_t)hi) >> 16);
*(dest+1) = ( ((uint32_t)lo << 16) | hi );
@ -246,6 +244,11 @@ void AudioOutputSPDIF::isr(void)
} while (dest < end);
}
#if IMXRT_CACHE_ENABLED >= 2
dest -= AUDIO_BLOCK_SAMPLES * 4/2 + 4/2;
arm_dcache_flush_delete(dest, sizeof(SPDIF_tx_buffer) / 2 );
#endif
}
void AudioOutputSPDIF::mute_PCM(const bool mute)
@ -299,7 +302,7 @@ void AudioOutputSPDIF::update(void)
}
#if defined(KINETISK)
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
// PLL is at 96 MHz in these modes
#define MCLK_MULT 2
@ -324,12 +327,17 @@ void AudioOutputSPDIF::update(void)
#define MCLK_MULT 1
#define MCLK_DIV 17
#elif F_CPU == 216000000
#define MCLK_MULT 8
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000
#define MCLK_MULT 4
#define MCLK_MULT 2
#define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 16000000
#define MCLK_MULT 12
#define MCLK_DIV 17
@ -344,10 +352,12 @@ void AudioOutputSPDIF::update(void)
#define MCLK_SRC 0 // system clock
#endif
#endif
#endif
PROGMEM
void AudioOutputSPDIF::config_SPDIF(void)
{
#if defined(KINETISK)
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
@ -371,14 +381,70 @@ void AudioOutputSPDIF::config_SPDIF(void)
#if 0
// configure pin mux for 3 clock signals (debug only)
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, 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 11, PTC6, I2S0_MCLK
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) 44.1kHz
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK 5.6 MHz
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK 11.43MHz
#endif
#elif defined(__IMXRT1052__) || defined(__IMXRT1062__)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK
int rsync = 0;
int tsync = 1;
// configure transmitter
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(0); // watermark
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S1_TCR3 = I2S_TCR3_TCE;
//4 Words per Frame 32 Bit Word-Length -> 128 Bit Frame-Length, MSB First:
I2S1_TCR4 = I2S_TCR4_FRSZ(3) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
//I2S1_RCSR = 0;
I2S1_RMR = 0;
//I2S1_RCSR = (1<<25); //Reset
I2S1_RCR1 = I2S_RCR1_RFW(0);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_TCR4_FRSZ(3) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
I2S1_RCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
#if 0
//debug only:
CORE_PIN23_CONFIG = 3; //1:MCLK 11.43MHz
CORE_PIN21_CONFIG = 3; //1:RX_BCLK 5.6 MHz
CORE_PIN20_CONFIG = 3; //1:RX_SYNC 44.1 KHz
// CORE_PIN6_CONFIG = 3; //1:TX_DATA0
// CORE_PIN7_CONFIG = 3; //1:RX_DATA0
#endif
#endif
}
#elif defined(KINETISL)
#if defined(KINETISL)
void AudioOutputSPDIF::update(void)
{

View file

@ -34,10 +34,10 @@ public:
AudioOutputSPDIF(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
friend class AudioInputSPDIF;
//friend class AudioInputSPDIF;
static void mute_PCM(const bool mute);
protected:
AudioOutputSPDIF(int dummy): AudioStream(2, inputQueueArray) {}
//AudioOutputSPDIF(int dummy): AudioStream(2, inputQueueArray) {}
static void config_SPDIF(void);
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;

View file

@ -0,0 +1,315 @@
/* SPDIF for Teensy 3.X
* Copyright (c) 2015, Frank Bösing, f.boesing@gmx.de,
* Thanks to KPC & Paul Stoffregen!
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
// 2015/08/23: (FB) added mute_PCM() - sets or unsets VALID in VUCP (and adjusts PARITY)
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include <Arduino.h>
#include "output_spdif2.h"
#include "utility/imxrt_hw.h"
audio_block_t * AudioOutputSPDIF2::block_left_1st = NULL;
audio_block_t * AudioOutputSPDIF2::block_right_1st = NULL;
audio_block_t * AudioOutputSPDIF2::block_left_2nd = NULL;
audio_block_t * AudioOutputSPDIF2::block_right_2nd = NULL;
uint16_t AudioOutputSPDIF2::block_left_offset = 0;
uint16_t AudioOutputSPDIF2::block_right_offset = 0;
bool AudioOutputSPDIF2::update_responsibility = false;
DMAChannel AudioOutputSPDIF2::dma(false);
extern uint16_t spdif_bmclookup[256];
DMAMEM __attribute__((aligned(32)))
static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
#define PREAMBLE_B (0xE8) //11101000
#define PREAMBLE_M (0xE2) //11100010
#define PREAMBLE_W (0xE4) //11100100
#define VUCP_VALID ((0xCC) << 24)
#define VUCP_INVALID ((0xD4) << 24)// To mute PCM, set VUCP = invalid.
uint32_t AudioOutputSPDIF2::vucp = VUCP_VALID;
PROGMEM
void AudioOutputSPDIF2::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
block_left_1st = NULL;
block_right_1st = NULL;
// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_SPDIF();
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
dma.TCD->SADDR = SPDIF_tx_buffer;
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = nbytes_mlno;
dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
dma.TCD->DADDR = &I2S2_TDR0;
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
update_responsibility = update_setup();
dma.enable();
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
dma.attachInterrupt(isr);
}
/*
http://www.hardwarebook.info/S/PDIF
1. To make it easier and a bit faster, the parity-bit is always the same.
- With a alternating parity we had to adjust the next subframe. Instead, use a bit from the aux-info as parity.
2. The buffer is filled with an offset of 1 byte, so the last parity (which is always 0 now (see 1.) ) is written as first byte.
-> A bit easier and faster to construct both subframes.
*/
void AudioOutputSPDIF2::isr(void)
{
static uint16_t frame = 0;
const int16_t *src;
int32_t *end, *dest;
audio_block_t *block;
uint32_t saddr, offset;
uint16_t sample, lo, hi, aux;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)SPDIF_tx_buffer + sizeof(SPDIF_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4];
if (AudioOutputSPDIF2::update_responsibility) AudioStream::update_all();
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = (int32_t *)SPDIF_tx_buffer;
end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
}
block = AudioOutputSPDIF2::block_left_1st;
if (block) {
offset = AudioOutputSPDIF2::block_left_offset;
src = &block->data[offset];
do {
sample = *src++;
//Subframe Channel 1
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
lo = spdif_bmclookup[(uint8_t) sample];
lo ^= (~((int16_t)hi) >> 16);
// 16 Bit sample:
*(dest+1) = ((uint32_t)lo << 16) | hi;
// 4 Bit Auxillary-audio-databits, the first used as parity
aux = (0xB333 ^ (((uint32_t)((int16_t)lo)) >> 17));
if (++frame > 191) {
// VUCP-Bits ("Valid, Subcode, Channelstatus, Parity) = 0 (0xcc) | Preamble (depends on Framno.) | Auxillary
*(dest+0) = vucp | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
frame = 0;
} else {
*(dest+0) = vucp | (PREAMBLE_M << 16 ) | aux;
}
dest += 4;
} while (dest < end);
offset += AUDIO_BLOCK_SAMPLES/2;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputSPDIF2::block_left_offset = offset;
} else {
AudioOutputSPDIF2::block_left_offset = 0;
AudioStream::release(block);
AudioOutputSPDIF2::block_left_1st = AudioOutputSPDIF2::block_left_2nd;
AudioOutputSPDIF2::block_left_2nd = NULL;
}
} else {
do {
if ( ++frame > 191 ) {
*(dest+0) = vucp | 0x00e8cccc;
frame = 0;
} else {
*(dest+0) = vucp | 0x00e2cccc;
}
*(dest+1) = 0xccccccccUL;
dest +=4;
} while (dest < end);
}
dest -= AUDIO_BLOCK_SAMPLES * 4/2 - 4/2;
block = AudioOutputSPDIF2::block_right_1st;
if (block) {
offset = AudioOutputSPDIF2::block_right_offset;
src = &block->data[offset];
do {
sample = *src++;
//Subframe Channel 2
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
lo = spdif_bmclookup[(uint8_t)sample];
lo ^= (~((int16_t)hi) >> 16);
*(dest+1) = ( ((uint32_t)lo << 16) | hi );
aux = (0xB333 ^ (((uint32_t)((int16_t)lo)) >> 17));
*(dest+0) = vucp | (PREAMBLE_W << 16 ) | aux;
dest += 4;
} while (dest < end);
offset += AUDIO_BLOCK_SAMPLES/2;
if (offset < AUDIO_BLOCK_SAMPLES) {
AudioOutputSPDIF2::block_right_offset = offset;
} else {
AudioOutputSPDIF2::block_right_offset = 0;
AudioStream::release(block);
AudioOutputSPDIF2::block_right_1st = AudioOutputSPDIF2::block_right_2nd;
AudioOutputSPDIF2::block_right_2nd = NULL;
}
} else {
do {
*dest = vucp | 0x00e4ccccUL;
*(dest+1) = 0xccccccccUL;
dest += 4 ;
} while (dest < end);
}
#if IMXRT_CACHE_ENABLED >= 2
dest -= AUDIO_BLOCK_SAMPLES * 4/2 + 4/2;
arm_dcache_flush_delete(dest, sizeof(SPDIF_tx_buffer) / 2 );
#endif
}
void AudioOutputSPDIF2::mute_PCM(const bool mute)
{
vucp = mute?VUCP_INVALID:VUCP_VALID;
}
void AudioOutputSPDIF2::update(void)
{
audio_block_t *block;
block = receiveReadOnly(0); // input 0 = left channel
if (block) {
__disable_irq();
if (block_left_1st == NULL) {
block_left_1st = block;
block_left_offset = 0;
__enable_irq();
} else if (block_left_2nd == NULL) {
block_left_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = block;
block_left_offset = 0;
__enable_irq();
release(tmp);
}
}
block = receiveReadOnly(1); // input 1 = right channel
if (block) {
__disable_irq();
if (block_right_1st == NULL) {
block_right_1st = block;
block_right_offset = 0;
__enable_irq();
} else if (block_right_2nd == NULL) {
block_right_2nd = block;
__enable_irq();
} else {
audio_block_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = block;
block_right_offset = 0;
__enable_irq();
release(tmp);
}
}
}
PROGMEM
void AudioOutputSPDIF2::config_SPDIF(void)
{
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
// configure transmitter
I2S2_TMR = 0;
I2S2_TCR1 = I2S_TCR1_RFW(0); // watermark
I2S2_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S2_TCR3 = I2S_TCR3_TCE;
//4 Words per Frame 32 Bit Word-Length -> 128 Bit Frame-Length, MSB First:
I2S2_TCR4 = I2S_TCR4_FRSZ(3) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
#if 0
//debug only:
CORE_PIN5_CONFIG = 2; //2:MCLK 11.43MHz
CORE_PIN4_CONFIG = 2; //2:TX_BCLK 5 MHz
CORE_PIN3_CONFIG = 2; //2:TX_SYNC 44.1 KHz
#endif
}
#endif

View file

@ -0,0 +1,57 @@
/* SPDIF for Teensy 3.X
* Copyright (c) 2015, Frank Bösing, f.boesing@gmx.de,
* Thanks to KPC & Paul Stoffregen!
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
#ifndef output_SPDIF2_h_
#define output_SPDIF2_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"
class AudioOutputSPDIF2 : public AudioStream
{
public:
AudioOutputSPDIF2(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
//friend class AudioInputSPDIF;
static void mute_PCM(const bool mute);
protected:
//AudioOutputSPDIF2(int dummy): AudioStream(2, inputQueueArray) {}
static void config_SPDIF(void);
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static uint32_t vucp;
static audio_block_t *block_left_2nd;
static audio_block_t *block_right_2nd;
static uint16_t block_left_offset;
static uint16_t block_right_offset;
audio_block_t *inputQueueArray[2];
};
#endif

View file

@ -0,0 +1,258 @@
/* Hardware-SPDIF for Teensy 4
* Copyright (c) 2019, Frank Bösing, f.boesing@gmx.de
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
/*
http://www.hardwarebook.info/S/PDIF
https://www.mikrocontroller.net/articles/S/PDIF
https://en.wikipedia.org/wiki/S/PDIF
*/
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include <Arduino.h>
#include "output_spdif3.h"
#include "utility/imxrt_hw.h"
#include "memcpy_audio.h"
#include <math.h>
audio_block_t * AudioOutputSPDIF3::block_left_1st = nullptr;
audio_block_t * AudioOutputSPDIF3::block_right_1st = nullptr;
audio_block_t * AudioOutputSPDIF3::block_left_2nd = nullptr;
audio_block_t * AudioOutputSPDIF3::block_right_2nd = nullptr;
bool AudioOutputSPDIF3::update_responsibility = false;
DMAChannel AudioOutputSPDIF3::dma(false);
DMAMEM __attribute__((aligned(32)))
static int32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4];
DMAMEM __attribute__((aligned(32)))
audio_block_t AudioOutputSPDIF3::block_silent;
PROGMEM
void AudioOutputSPDIF3::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
block_left_1st = nullptr;
block_right_1st = nullptr;
memset(&block_silent, 0, sizeof(block_silent));
uint32_t fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
// n1, n2 choosen for compatibility with I2S (same PLL frequency) :
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
double pllclock = (c0 + (double)c1 / c2) * 24000000; //677376000 Hz
//use new pred/podf values
n1 = 7; //0: divide by 1 (do not use with high input frequencies), 1:/2, 2: /3, 7:/8
n2 = 0; //0: divide by 1, 7: divide by 8
uint32_t clock = pllclock / (1 + n1) / (1 + n2);
uint32_t clkdiv = clock / (fs * 64); // 1 .. 128
uint32_t mod = clock % (fs * 64);
if (mod > ((fs * 64) / 2)) clkdiv += 1; //nearest divider
CCM_CCGR5 &= ~CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate off
CCM_CDCDR = (CCM_CDCDR & ~(CCM_CDCDR_SPDIF0_CLK_SEL_MASK | CCM_CDCDR_SPDIF0_CLK_PRED_MASK | CCM_CDCDR_SPDIF0_CLK_PODF_MASK))
| CCM_CDCDR_SPDIF0_CLK_SEL(0) // 0 PLL4, 1 PLL3 PFD2, 2 PLL5, 3 pll3_sw_clk
| CCM_CDCDR_SPDIF0_CLK_PRED(n1)
| CCM_CDCDR_SPDIF0_CLK_PODF(n2);
CCM_CCGR5 |= CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate on
if ((SPDIF_SCR & 0x400) != 0) { //If default value:
SPDIF_SCR = SPDIF_SCR_SOFT_RESET; //Reset SPDIF
while (SPDIF_SCR & SPDIF_SCR_SOFT_RESET) {;} //Wait for Reset (takes 8 cycles)
SPDIF_SCR = 0;
}
SPDIF_SCR |=
SPDIF_SCR_RXFIFOFULL_SEL(0) |// Full interrupt if at least 1 sample in Rx left and right FIFOs
SPDIF_SCR_RXAUTOSYNC |
SPDIF_SCR_TXAUTOSYNC |
SPDIF_SCR_TXFIFOEMPTY_SEL(3) | // Empty interrupt if at most 4 sample in Tx left and right FIFOs
SPDIF_SCR_TXFIFO_CTRL(1) | // 0:Send zeros 1: normal operation
SPDIF_SCR_DMA_TX_EN |
//SPDIF_SCR_VALCTRL | // Outgoing Validity always clear
SPDIF_SCR_TXSEL(5) | //0:off and output 0, 1:Feed-though SPDIFIN, 5:Tx Normal operation
SPDIF_SCR_USRC_SEL(3); // No embedded U channel
SPDIF_SRCD = 0;
SPDIF_STCSCH = 0;
SPDIF_STCSCL = 0;
SPDIF_SRCD = 0;
SPDIF_STC = SPDIF_STC_TXCLK_SOURCE(1) //tx_clk input (from SPDIF0_CLK_ROOT)
| SPDIF_STC_SYSCLK_DF(0)
| SPDIF_STC_TXCLK_DF(clkdiv - 1);
const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
dma.TCD->SADDR = SPDIF_tx_buffer;
dma.TCD->SOFF = 4;
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
dma.TCD->NBYTES_MLNO = DMA_TCD_NBYTES_MLOFFYES_NBYTES(nbytes_mlno) | DMA_TCD_NBYTES_DMLOE |
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8);
dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
dma.TCD->DADDR = &SPDIF_STL;
dma.TCD->DOFF = 4;
dma.TCD->DLASTSGA = -8;
//dma.TCD->ATTR_DST = ((31 - __builtin_clz(8)) << 3);
dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPDIF_TX);
update_responsibility = update_setup();
dma.enable();
dma.attachInterrupt(isr);
CORE_PIN14_CONFIG = 3; //3:SPDIF_OUT
SPDIF_STC |= SPDIF_STC_TX_ALL_CLK_EN;
// pinMode(13, OUTPUT);
}
void AudioOutputSPDIF3::isr(void)
{
const int16_t *src_left, *src_right;
const int32_t *end;
int32_t *dest;
audio_block_t *block_left, *block_right;
uint32_t saddr;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)SPDIF_tx_buffer + sizeof(SPDIF_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*2;
end = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*4;
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = SPDIF_tx_buffer;
end = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*2;
}
block_left = block_left_1st;
if (!block_left) block_left = &block_silent;
block_right = block_right_1st;
if (!block_right) block_right = &block_silent;
src_left = (const int16_t *)(block_left->data);
src_right = (const int16_t *)(block_right->data);
do {
*dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8;
*dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8;
*dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8;
*dest++ = (*src_left++) << 8;
*dest++ = (*src_right++) << 8;
#if IMXRT_CACHE_ENABLED >= 2
SCB_CACHE_DCCIMVAC = (uint32_t) dest - 32;
#endif
} while (dest < end);
if (block_left != &block_silent) {
release(block_left);
block_left_1st = block_left_2nd;
block_left_2nd = nullptr;
}
if (block_right != &block_silent) {
release(block_right);
block_right_1st = block_right_2nd;
block_right_2nd = nullptr;
}
if (update_responsibility) update_all();
//digitalWriteFast(13,!digitalReadFast(13));
}
void AudioOutputSPDIF3::update(void)
{
audio_block_t *block_left, *block_right;
block_left = receiveReadOnly(0); // input 0
block_right = receiveReadOnly(1); // input 1
__disable_irq();
if (block_left) {
if (block_left_1st == nullptr) {
block_left_1st = block_left;
block_left = nullptr;
} else if (block_left_2nd == nullptr) {
block_left_2nd = block_left;
block_left = nullptr;
} else {
audio_block_t *tmp = block_left_1st;
block_left_1st = block_left_2nd;
block_left_2nd = block_left;
block_left = tmp;
}
}
if (block_right) {
if (block_right_1st == nullptr) {
block_right_1st = block_right;
block_right = nullptr;
} else if (block_right_2nd == nullptr) {
block_right_2nd = block_right;
block_right = nullptr;
} else {
audio_block_t *tmp = block_right_1st;
block_right_1st = block_right_2nd;
block_right_2nd = block_right;
block_right = tmp;
}
}
__enable_irq();
if (block_left) {
release(block_left);
}
if (block_right) {
release(block_right);
}
}
void AudioOutputSPDIF3::mute_PCM(const bool mute)
{
if (mute)
SPDIF_SCR |= SPDIF_SCR_VALCTRL;
else
SPDIF_SCR &= ~SPDIF_SCR_VALCTRL;
}
#endif

View file

@ -0,0 +1,53 @@
/* Hardware-SPDIF for Teensy 4
* Copyright (c) 2019, Frank Bösing, f.boesing@gmx.de
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
#ifndef output_SPDIF3_h_
#define output_SPDIF3_h_
#include <Arduino.h>
#include <AudioStream.h>
#include <DMAChannel.h>
class AudioOutputSPDIF3 : public AudioStream
{
public:
AudioOutputSPDIF3(void) : AudioStream(2, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
//friend class AudioInputSPDIF;
static void mute_PCM(const bool mute);
protected:
//AudioOutputSPDIF3(int dummy): AudioStream(2, inputQueueArray) {}
static audio_block_t *block_left_1st;
static audio_block_t *block_right_1st;
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
static audio_block_t *block_left_2nd;
static audio_block_t *block_right_2nd;
static audio_block_t block_silent;
audio_block_t *inputQueueArray[2];
};
#endif

View file

@ -27,27 +27,31 @@
#include <Arduino.h>
#include "output_tdm.h"
#include "memcpy_audio.h"
#if defined(KINETISK)
#include "utility/imxrt_hw.h"
audio_block_t * AudioOutputTDM::block_input[16] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
bool AudioOutputTDM::update_responsibility = false;
static uint32_t zeros[AUDIO_BLOCK_SAMPLES/2];
DMAMEM static uint32_t tdm_tx_buffer[AUDIO_BLOCK_SAMPLES*16];
DMAChannel AudioOutputTDM::dma(false);
DMAMEM __attribute__((aligned(32)))
static uint32_t zeros[AUDIO_BLOCK_SAMPLES/2];
DMAMEM __attribute__((aligned(32)))
static uint32_t tdm_tx_buffer[AUDIO_BLOCK_SAMPLES*16];
void AudioOutputTDM::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
for (int i=0; i < 16; i++) {
block_input[i] = NULL;
block_input[i] = nullptr;
}
// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_tdm();
#if defined(KINETISK)
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
dma.TCD->SADDR = tdm_tx_buffer;
@ -62,11 +66,35 @@ void AudioOutputTDM::begin(void)
dma.TCD->BITER_ELINKNO = sizeof(tdm_tx_buffer) / 4;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
update_responsibility = update_setup();
dma.enable();
I2S0_TCSR = I2S_TCSR_SR;
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#elif defined(__IMXRT1062__)
CORE_PIN7_CONFIG = 3; //1:TX_DATA0
dma.TCD->SADDR = tdm_tx_buffer;
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 = -sizeof(tdm_tx_buffer);
dma.TCD->DADDR = &I2S1_TDR0;
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(tdm_tx_buffer) / 4;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(tdm_tx_buffer) / 4;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI1_TX);
update_responsibility = update_setup();
dma.enable();
I2S1_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE;
I2S1_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
#endif
dma.attachInterrupt(isr);
}
@ -75,14 +103,23 @@ static void memcpy_tdm_tx(uint32_t *dest, const uint32_t *src1, const uint32_t *
{
uint32_t i, in1, in2, out1, out2;
for (i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
for (i=0; i < AUDIO_BLOCK_SAMPLES/4; i++) {
in1 = *src1++;
in2 = *src2++;
out1 = (in1 << 16) | (in2 & 0xFFFF);
out2 = (in1 & 0xFFFF0000) | (in2 >> 16);
*dest = out1;
*(dest + 8) = out2;
dest += 16;
in1 = *src1++;
in2 = *src2++;
out1 = (in1 << 16) | (in2 & 0xFFFF);
out2 = (in1 & 0xFFFF0000) | (in2 >> 16);
*(dest + 16)= out1;
*(dest + 24) = out2;
dest += 32;
}
}
@ -104,16 +141,26 @@ void AudioOutputTDM::isr(void)
dest = tdm_tx_buffer;
}
if (update_responsibility) AudioStream::update_all();
#if IMXRT_CACHE_ENABLED >= 2
uint32_t *dc = dest;
#endif
for (i=0; i < 16; i += 2) {
src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros;
src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros;
memcpy_tdm_tx(dest, src1, src2);
dest++;
}
#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(dc, sizeof(tdm_tx_buffer) / 2 );
#endif
for (i=0; i < 16; i++) {
if (block_input[i]) {
release(block_input[i]);
block_input[i] = NULL;
block_input[i] = nullptr;
}
}
}
@ -135,7 +182,7 @@ void AudioOutputTDM::update(void)
}
}
#if defined(KINETISK)
// MCLK needs to be 48e6 / 1088 * 512 = 22.588235 MHz -> 44.117647 kHz sample rate
//
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
@ -162,12 +209,17 @@ void AudioOutputTDM::update(void)
#define MCLK_MULT 2
#define MCLK_DIV 17
#elif F_CPU == 216000000
#define MCLK_MULT 16
#define MCLK_DIV 153
#define MCLK_SRC 0
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#elif F_CPU == 240000000
#define MCLK_MULT 8
#define MCLK_MULT 2
#define MCLK_DIV 85
#define MCLK_SRC 0
#elif F_CPU == 256000000
#define MCLK_MULT 12
#define MCLK_DIV 17
#define MCLK_SRC 1
#else
#error "This CPU Clock Speed is not supported by the Audio library";
#endif
@ -179,9 +231,11 @@ void AudioOutputTDM::update(void)
#define MCLK_SRC 0 // system clock
#endif
#endif
#endif
void AudioOutputTDM::config_tdm(void)
{
#if defined(KINETISK)
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
@ -216,11 +270,65 @@ void AudioOutputTDM::config_tdm(void)
I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
// configure pin mux for 3 clock signals
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, 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 11, PTC6, I2S0_MCLK
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK) - 44.1kHz
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK - 11.2 MHz
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK - 22.5 MHz
#elif defined(__IMXRT1062__)
CCM_CCGR5 |= CCM_CCGR5_SAI1(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing
if (I2S1_TCSR & I2S_TCSR_TE) return;
if (I2S1_RCSR & I2S_RCSR_RE) return;
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT;
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI1_CLK_SEL_MASK))
| CCM_CSCMR1_SAI1_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
n1 = n1 / 2; //Double Speed for TDM
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI1_CLK_PRED_MASK | CCM_CS1CDR_SAI1_CLK_PODF_MASK))
| CCM_CS1CDR_SAI1_CLK_PRED(n1-1) // &0x07
| CCM_CS1CDR_SAI1_CLK_PODF(n2-1); // &0x3f
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI1_MCLK_DIR | IOMUXC_GPR_GPR1_SAI1_MCLK1_SEL(0)); //Select MCLK
// configure transmitter
int rsync = 0;
int tsync = 1;
I2S1_TMR = 0;
I2S1_TCR1 = I2S_TCR1_RFW(4);
I2S1_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
| I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S1_TCR3 = I2S_TCR3_TCE;
I2S1_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSD;
I2S1_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
I2S1_RMR = 0;
I2S1_RCR1 = I2S_RCR1_RFW(4);
I2S1_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
| I2S_RCR2_BCD | I2S_RCR2_DIV(0);
I2S1_RCR3 = I2S_RCR3_RCE;
I2S1_RCR4 = I2S_RCR4_FRSZ(7) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSD;
I2S1_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
CORE_PIN23_CONFIG = 3; //1:MCLK
CORE_PIN21_CONFIG = 3; //1:RX_BCLK
CORE_PIN20_CONFIG = 3; //1:RX_SYNC
#endif
}
#endif // KINETISK

View file

@ -0,0 +1,220 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1062__)
#include <Arduino.h>
#include "output_tdm2.h"
#include "memcpy_audio.h"
#include "utility/imxrt_hw.h"
audio_block_t * AudioOutputTDM2::block_input[16] = {
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
};
bool AudioOutputTDM2::update_responsibility = false;
DMAChannel AudioOutputTDM2::dma(false);
DMAMEM __attribute__((aligned(32)))
static uint32_t zeros[AUDIO_BLOCK_SAMPLES/2];
DMAMEM __attribute__((aligned(32)))
static uint32_t tdm_tx_buffer[AUDIO_BLOCK_SAMPLES*16];
void AudioOutputTDM2::begin(void)
{
dma.begin(true); // Allocate the DMA channel first
for (int i=0; i < 16; i++) {
block_input[i] = nullptr;
}
// TODO: should we set & clear the I2S_TCSR_SR bit here?
config_tdm();
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
dma.TCD->SADDR = tdm_tx_buffer;
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 = -sizeof(tdm_tx_buffer);
dma.TCD->DADDR = &I2S2_TDR0;
dma.TCD->DOFF = 0;
dma.TCD->CITER_ELINKNO = sizeof(tdm_tx_buffer) / 4;
dma.TCD->DLASTSGA = 0;
dma.TCD->BITER_ELINKNO = sizeof(tdm_tx_buffer) / 4;
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
update_responsibility = update_setup();
dma.enable();
//I2S2_RCSR |= I2S_RCSR_RE;
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
dma.attachInterrupt(isr);
}
// TODO: needs optimization...
static void memcpy_tdm_tx(uint32_t *dest, const uint32_t *src1, const uint32_t *src2)
{
uint32_t i, in1, in2, out1, out2;
for (i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
in1 = *src1++;
in2 = *src2++;
out1 = (in1 << 16) | (in2 & 0xFFFF);
out2 = (in1 & 0xFFFF0000) | (in2 >> 16);
*dest = out1;
*(dest + 8) = out2;
in1 = *src1++;
in2 = *src2++;
out1 = (in1 << 16) | (in2 & 0xFFFF);
out2 = (in1 & 0xFFFF0000) | (in2 >> 16);
*(dest + 16)= out1;
*(dest + 24) = out2;
dest += 32;
}
}
void AudioOutputTDM2::isr(void)
{
uint32_t *dest, *dc;
const uint32_t *src1, *src2;
uint32_t i, saddr;
saddr = (uint32_t)(dma.TCD->SADDR);
dma.clearInterrupt();
if (saddr < (uint32_t)tdm_tx_buffer + sizeof(tdm_tx_buffer) / 2) {
// DMA is transmitting the first half of the buffer
// so we must fill the second half
dest = tdm_tx_buffer + AUDIO_BLOCK_SAMPLES*8;
} else {
// DMA is transmitting the second half of the buffer
// so we must fill the first half
dest = tdm_tx_buffer;
}
if (update_responsibility) AudioStream::update_all();
dc = dest;
for (i=0; i < 16; i += 2) {
src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros;
src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros;
memcpy_tdm_tx(dest, src1, src2);
dest++;
}
#if IMXRT_CACHE_ENABLED >= 2
arm_dcache_flush_delete(dc, sizeof(tdm_tx_buffer) / 2 );
#endif
for (i=0; i < 16; i++) {
if (block_input[i]) {
release(block_input[i]);
block_input[i] = nullptr;
}
}
}
void AudioOutputTDM2::update(void)
{
audio_block_t *prev[16];
unsigned int i;
__disable_irq();
for (i=0; i < 16; i++) {
prev[i] = block_input[i];
block_input[i] = receiveReadOnly(i);
}
__enable_irq();
for (i=0; i < 16; i++) {
if (prev[i]) release(prev[i]);
}
}
void AudioOutputTDM2::config_tdm(void)
{
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
// if either transmitter or receiver is enabled, do nothing
if (I2S2_TCSR & I2S_TCSR_TE) return;
if (I2S2_RCSR & I2S_RCSR_RE) return;
//PLL:
int fs = AUDIO_SAMPLE_RATE_EXACT; //176.4 khZ
// PLL between 27*24 = 648MHz und 54*24=1296MHz
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
double C = ((double)fs * 256 * n1 * n2) / 24000000;
int c0 = C;
int c2 = 10000;
int c1 = C * c2 - (c0 * c2);
set_audioClock(c0, c1, c2);
// clear SAI1_CLK register locations
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
n1 = n1 / 2; //Double Speed for TDM
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1) // &0x07
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1); // &0x3f
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
// configure transmitter
int rsync = 1;
int tsync = 0;
I2S2_TMR = 0;
I2S2_TCR1 = I2S_TCR1_RFW(4);
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
| I2S_TCR2_BCD | I2S_TCR2_DIV(0);
I2S2_TCR3 = I2S_TCR3_TCE;
I2S2_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSD;
I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
// configure receiver (sync'd to transmitter clocks)
I2S2_RMR = 0;
I2S2_RCR1 = I2S_RCR1_RFW(4);
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
| I2S_RCR2_BCD | I2S_RCR2_DIV(0);
I2S2_RCR3 = I2S_RCR3_RCE;
I2S2_RCR4 = I2S_RCR4_FRSZ(7) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSD;
I2S2_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
CORE_PIN33_CONFIG = 2; //2:MCLK
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
}
#endif

View file

@ -0,0 +1,54 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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 defined(__IMXRT1052__) || defined(__IMXRT1062__)
#ifndef output_tdm2_h_
#define output_tdm2_h_
#include "Arduino.h"
#include "AudioStream.h"
#include "DMAChannel.h"
class AudioOutputTDM2 : public AudioStream
{
public:
AudioOutputTDM2(void) : AudioStream(16, inputQueueArray) { begin(); }
virtual void update(void);
void begin(void);
friend class AudioInputTDM2;
protected:
static void config_tdm(void);
static audio_block_t *block_input[16];
static bool update_responsibility;
static DMAChannel dma;
static void isr(void);
private:
audio_block_t *inputQueueArray[16];
};
#endif
#endif

View file

@ -50,11 +50,11 @@ bool AudioPlaySdRaw::play(const char *filename)
__enable_irq();
if (!rawfile) {
//Serial.println("unable to open file");
#if defined(HAS_KINETIS_SDHC)
#if defined(HAS_KINETIS_SDHC)
if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
#else
#else
AudioStopUsingSPI();
#endif
#endif
return false;
}
file_size = rawfile.size();
@ -71,11 +71,11 @@ void AudioPlaySdRaw::stop(void)
playing = false;
__enable_irq();
rawfile.close();
#if defined(HAS_KINETIS_SDHC)
#if defined(HAS_KINETIS_SDHC)
if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
#else
#else
AudioStopUsingSPI();
#endif
#endif
} else {
__enable_irq();
}
@ -104,11 +104,11 @@ void AudioPlaySdRaw::update(void)
transmit(block);
} else {
rawfile.close();
#if defined(HAS_KINETIS_SDHC)
#if defined(HAS_KINETIS_SDHC)
if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
#else
#else
AudioStopUsingSPI();
#endif
#endif
playing = false;
}
release(block);

View file

@ -71,11 +71,11 @@ bool AudioPlaySdWav::play(const char *filename)
wavfile.open(filename);
__enable_irq();
if (!wavfile) {
#if defined(HAS_KINETIS_SDHC)
#if defined(HAS_KINETIS_SDHC)
if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
#else
#else
AudioStopUsingSPI();
#endif
#endif
return false;
}
buffer_length = 0;
@ -100,11 +100,11 @@ void AudioPlaySdWav::stop(void)
if (b1) release(b1);
if (b2) release(b2);
wavfile.close();
#if defined(HAS_KINETIS_SDHC)
#if defined(HAS_KINETIS_SDHC)
if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
#else
#else
AudioStopUsingSPI();
#endif
#endif
} else {
__enable_irq();
}
@ -142,7 +142,7 @@ void AudioPlaySdWav::update(void)
n = buffer_length - buffer_offset;
if (n > 0) {
// we have buffered data
if (consume(n)) return; // it was enough to transmit audio
if (consume(n)) return; // it was enough to transmit audio
}
// we only get to this point when buffer[512] is empty
@ -291,7 +291,7 @@ start:
break;
// find the data chunk
case STATE_PARSE3: // 10
case STATE_PARSE3: // 10
len = data_length;
if (size < len) len = size;
memcpy((uint8_t *)header + header_offset, p, len);
@ -327,7 +327,7 @@ start:
goto start;
// ignore any extra unknown chunks (title & artist info)
case STATE_PARSE4: // 11
case STATE_PARSE4: // 11
if (size < data_length) {
data_length -= size;
buffer_offset += size;
@ -479,17 +479,17 @@ right16:
/*
00000000 52494646 66EA6903 57415645 666D7420 RIFFf.i.WAVEfmt
00000010 10000000 01000200 44AC0000 10B10200 ........D.......
00000020 04001000 4C495354 3A000000 494E464F ....LIST:...INFO
00000030 494E414D 14000000 49205761 6E742054 INAM....I Want T
00000040 6F20436F 6D65204F 76657200 49415254 o Come Over.IART
00000050 12000000 4D656C69 73736120 45746865 ....Melissa Ethe
00000060 72696467 65006461 746100EA 69030100 ridge.data..i...
00000070 FEFF0300 FCFF0400 FDFF0200 0000FEFF ................
00000080 0300FDFF 0200FFFF 00000100 FEFF0300 ................
00000090 FDFF0300 FDFF0200 FFFF0100 0000FFFF ................
*/
00000000 52494646 66EA6903 57415645 666D7420 RIFFf.i.WAVEfmt
00000010 10000000 01000200 44AC0000 10B10200 ........D.......
00000020 04001000 4C495354 3A000000 494E464F ....LIST:...INFO
00000030 494E414D 14000000 49205761 6E742054 INAM....I Want T
00000040 6F20436F 6D65204F 76657200 49415254 o Come Over.IART
00000050 12000000 4D656C69 73736120 45746865 ....Melissa Ethe
00000060 72696467 65006461 746100EA 69030100 ridge.data..i...
00000070 FEFF0300 FCFF0400 FDFF0200 0000FEFF ................
00000080 0300FDFF 0200FFFF 00000100 FEFF0300 ................
00000090 FDFF0300 FDFF0200 FFFF0100 0000FFFF ................
*/

View file

@ -34,7 +34,7 @@
class AudioPlaySdWav : public AudioStream
{
public:
AudioPlaySdWav(void) : AudioStream(0, NULL) { begin(); }
AudioPlaySdWav(void) : AudioStream(0, NULL), block_left(NULL), block_right(NULL) { begin(); }
void begin(void);
bool play(const char *filename);
void stop(void);

View file

@ -33,7 +33,7 @@
// compute (a - b) / c
// handling 32 bit interger overflow at every step
// without resorting to slow 64 bit math
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
static inline int32_t substract_int32_then_divide_int32(int32_t a, int32_t b, int32_t c) __attribute__((always_inline, unused));
static inline int32_t substract_int32_then_divide_int32(int32_t a, int32_t b, int32_t c)
{

View file

@ -29,7 +29,7 @@
#include "utility/dspinst.h"
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
void AudioSynthWaveformPWM::update(void)
{

View file

@ -52,7 +52,7 @@ void AudioSynthWaveformSine::update(void)
scale = (ph >> 8) & 0xFFFF;
val2 *= scale;
val1 *= 0x10000 - scale;
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
block->data[i] = multiply_32x32_rshift32(val1 + val2, magnitude);
#elif defined(KINETISL)
block->data[i] = (((val1 + val2) >> 16) * magnitude) >> 16;
@ -72,7 +72,7 @@ void AudioSynthWaveformSine::update(void)
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
// High accuracy 11th order Taylor Series Approximation
// input is 0 to 0xFFFFFFFF, representing 0 to 360 degree phase
// output is 32 bit signed integer, top 25 bits should be very good
@ -104,7 +104,7 @@ static int32_t taylor(uint32_t ph)
void AudioSynthWaveformSineHires::update(void)
{
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
audio_block_t *msw, *lsw;
uint32_t i, ph, inc;
int32_t val;
@ -138,7 +138,7 @@ void AudioSynthWaveformSineHires::update(void)
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
void AudioSynthWaveformSineModulated::update(void)
{

View file

@ -221,7 +221,7 @@ void AudioSynthWaveformModulated::update(void)
n = n << 1;
#else
// exp2 algorithm by Laurent de Soras
// http://www.musicdsp.org/showone.php?id=106
// https://www.musicdsp.org/en/latest/Other/106-fast-exp2-approximation.html
n = (n + 134217728) << 3;
n = multiply_32x32_rshift32_rounded(n, n);
n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;

View file

@ -45,7 +45,7 @@ void AudioSynthNoiseWhite::update(void)
end = p + AUDIO_BLOCK_SAMPLES/2;
lo = seed;
do {
#if defined(KINETISK)
#if defined(__ARM_ARCH_7EM__)
hi = multiply_16bx16t(16807, lo); // 16807 * (lo >> 16)
lo = 16807 * (lo & 0xFFFF);
lo += (hi & 0x7FFF) << 16;

View file

@ -33,7 +33,7 @@
static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift) __attribute__((always_inline, unused));
static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("ssat %0, %1, %2, asr %3" : "=r" (out) : "I" (bits), "r" (val), "I" (rshift));
return out;
@ -54,7 +54,7 @@ static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift)
static inline int16_t saturate16(int32_t val) __attribute__((always_inline, unused));
static inline int16_t saturate16(int32_t val)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int16_t out;
int32_t tmp;
asm volatile("ssat %0, %1, %2" : "=r" (tmp) : "I" (16), "r" (val) );
@ -69,7 +69,7 @@ static inline int16_t saturate16(int32_t val)
static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b) __attribute__((always_inline, unused));
static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("smulwb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
return out;
@ -82,7 +82,7 @@ static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b)
static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b) __attribute__((always_inline, unused));
static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("smulwt %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
return out;
@ -95,7 +95,7 @@ static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b)
static inline int32_t multiply_32x32_rshift32(int32_t a, int32_t b) __attribute__((always_inline, unused));
static inline int32_t multiply_32x32_rshift32(int32_t a, int32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("smmul %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
return out;
@ -108,7 +108,7 @@ static inline int32_t multiply_32x32_rshift32(int32_t a, int32_t b)
static inline int32_t multiply_32x32_rshift32_rounded(int32_t a, int32_t b) __attribute__((always_inline, unused));
static inline int32_t multiply_32x32_rshift32_rounded(int32_t a, int32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("smmulr %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
return out;
@ -121,7 +121,7 @@ static inline int32_t multiply_32x32_rshift32_rounded(int32_t a, int32_t b)
static inline int32_t multiply_accumulate_32x32_rshift32_rounded(int32_t sum, int32_t a, int32_t b) __attribute__((always_inline, unused));
static inline int32_t multiply_accumulate_32x32_rshift32_rounded(int32_t sum, int32_t a, int32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("smmlar %0, %2, %3, %1" : "=r" (out) : "r" (sum), "r" (a), "r" (b));
return out;
@ -134,7 +134,7 @@ static inline int32_t multiply_accumulate_32x32_rshift32_rounded(int32_t sum, in
static inline int32_t multiply_subtract_32x32_rshift32_rounded(int32_t sum, int32_t a, int32_t b) __attribute__((always_inline, unused));
static inline int32_t multiply_subtract_32x32_rshift32_rounded(int32_t sum, int32_t a, int32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("smmlsr %0, %2, %3, %1" : "=r" (out) : "r" (sum), "r" (a), "r" (b));
return out;
@ -148,7 +148,7 @@ static inline int32_t multiply_subtract_32x32_rshift32_rounded(int32_t sum, int3
static inline uint32_t pack_16t_16t(int32_t a, int32_t b) __attribute__((always_inline, unused));
static inline uint32_t pack_16t_16t(int32_t a, int32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("pkhtb %0, %1, %2, asr #16" : "=r" (out) : "r" (a), "r" (b));
return out;
@ -161,7 +161,7 @@ static inline uint32_t pack_16t_16t(int32_t a, int32_t b)
static inline uint32_t pack_16t_16b(int32_t a, int32_t b) __attribute__((always_inline, unused));
static inline uint32_t pack_16t_16b(int32_t a, int32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("pkhtb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
return out;
@ -174,7 +174,7 @@ static inline uint32_t pack_16t_16b(int32_t a, int32_t b)
static inline uint32_t pack_16b_16b(int32_t a, int32_t b) __attribute__((always_inline, unused));
static inline uint32_t pack_16b_16b(int32_t a, int32_t b)
{
#if defined(KINETISK)
#if defined (__ARM_ARCH_7EM__)
int32_t out;
asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (out) : "r" (b), "r" (a));
return out;

View file

@ -0,0 +1,56 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2019, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
/*
(c) Frank B
*/
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
#include "imxrt_hw.h"
PROGMEM
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4
{
if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
| CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
| CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
const int div_post_pll = 1; // other values: 2,4
CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass
}
#endif

View file

@ -0,0 +1,48 @@
/* Audio Library for Teensy 3.X
* Copyright (c) 2019, Paul Stoffregen, paul@pjrc.com
*
* Development of this audio library was funded by PJRC.COM, LLC by sales of
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
* open source software by purchasing Teensy or other PJRC products.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice, development funding notice, and this permission
* notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", 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.
*/
/*
(c) Frank B
*/
#if defined(__IMXRT1062__)
#ifndef imxr_hw_h_
#define imxr_hw_h_
#define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB
#include <Arduino.h>
#include <imxrt.h>
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force = false); // sets PLL4
#endif
#else
//No IMXRT
#define IMXRT_CACHE_ENABLED 0
#endif

View file

@ -23,7 +23,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#if !defined(__IMXRT1052__) && !defined(__IMXRT1062__)
#ifndef pdb_h_
#define pdb_h_
@ -71,3 +71,4 @@
#endif
#endif
#endif

View file

@ -15,18 +15,42 @@ env_default = teensy35
lib_ignore = Audio, SD
lib_deps =
721@3.0.2 ; TaskScheduler
322@1.0.7 ; SdFat
322 ; SdFat
[env:teensy35]
platform = teensy@3.6.0
platform = teensy
board = teensy35
framework = arduino
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
[env:teensy36]
platform = teensy@3.6.0
platform = teensy
board = teensy36
framework = arduino
lib_deps = ${common.lib_deps}
lib_ignore = ${common.lib_ignore}
; [platformio]
; env_default = teensy35
;
; [common]
; lib_ignore = Audio, SD
; lib_deps =
; 721@3.0.2 ; TaskScheduler
; 322@1.0.7 ; SdFat
;
; [env:teensy35]
; platform = teensy@3.6.0
; board = teensy35
; framework = arduino
; lib_deps = ${common.lib_deps}
; lib_ignore = ${common.lib_ignore}
;
; [env:teensy36]
; platform = teensy@3.6.0
; board = teensy36
; framework = arduino
; lib_deps = ${common.lib_deps}
; lib_ignore = ${common.lib_ignore}