diff --git a/sampler/lib/Audio_SdFat/.gitignore b/sampler/lib/Audio_SdFat/.gitignore deleted file mode 100644 index 1377554..0000000 --- a/sampler/lib/Audio_SdFat/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.swp diff --git a/sampler/lib/Audio_SdFat/.library.json b/sampler/lib/Audio_SdFat/.library.json deleted file mode 100644 index 01af93b..0000000 --- a/sampler/lib/Audio_SdFat/.library.json +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/sampler/lib/Audio_SdFat/Audio.h b/sampler/lib/Audio_SdFat/Audio.h index 90a3ccf..34ff1ed 100644 --- a/sampler/lib/Audio_SdFat/Audio.h +++ b/sampler/lib/Audio_SdFat/Audio.h @@ -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" diff --git a/sampler/lib/Audio_SdFat/analyze_fft1024.cpp b/sampler/lib/Audio_SdFat/analyze_fft1024.cpp index e0497bc..c03b564 100644 --- a/sampler/lib/Audio_SdFat/analyze_fft1024.cpp +++ b/sampler/lib/Audio_SdFat/analyze_fft1024.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/analyze_rms.cpp b/sampler/lib/Audio_SdFat/analyze_rms.cpp index 1cb1b3b..4a607aa 100644 --- a/sampler/lib/Audio_SdFat/analyze_rms.cpp +++ b/sampler/lib/Audio_SdFat/analyze_rms.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/analyze_tonedetect.cpp b/sampler/lib/Audio_SdFat/analyze_tonedetect.cpp index 3356490..82fc525 100644 --- a/sampler/lib/Audio_SdFat/analyze_tonedetect.cpp +++ b/sampler/lib/Audio_SdFat/analyze_tonedetect.cpp @@ -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) diff --git a/sampler/lib/Audio_SdFat/control_cs42448.cpp b/sampler/lib/Audio_SdFat/control_cs42448.cpp index 94683c2..91393ae 100644 --- a/sampler/lib/Audio_SdFat/control_cs42448.cpp +++ b/sampler/lib/Audio_SdFat/control_cs42448.cpp @@ -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 diff --git a/sampler/lib/Audio_SdFat/control_sgtl5000.cpp b/sampler/lib/Audio_SdFat/control_sgtl5000.cpp index 68d84c4..4f3517c 100644 --- a/sampler/lib/Audio_SdFat/control_sgtl5000.cpp +++ b/sampler/lib/Audio_SdFat/control_sgtl5000.cpp @@ -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 diff --git a/sampler/lib/Audio_SdFat/control_wm8731.cpp b/sampler/lib/Audio_SdFat/control_wm8731.cpp index 99ead18..a0ea25c 100644 --- a/sampler/lib/Audio_SdFat/control_wm8731.cpp +++ b/sampler/lib/Audio_SdFat/control_wm8731.cpp @@ -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) diff --git a/sampler/lib/Audio_SdFat/data_spdif.c b/sampler/lib/Audio_SdFat/data_spdif.c new file mode 100644 index 0000000..a439359 --- /dev/null +++ b/sampler/lib/Audio_SdFat/data_spdif.c @@ -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 + +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 +}; diff --git a/sampler/lib/Audio_SdFat/effect_freeverb.cpp b/sampler/lib/Audio_SdFat/effect_freeverb.cpp index b184b77..9950994 100644 --- a/sampler/lib/Audio_SdFat/effect_freeverb.cpp +++ b/sampler/lib/Audio_SdFat/effect_freeverb.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/effect_midside.cpp b/sampler/lib/Audio_SdFat/effect_midside.cpp index db581b2..bbfcfa4 100644 --- a/sampler/lib/Audio_SdFat/effect_midside.cpp +++ b/sampler/lib/Audio_SdFat/effect_midside.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/effect_multiply.cpp b/sampler/lib/Audio_SdFat/effect_multiply.cpp index 9220a5b..d3527b5 100644 --- a/sampler/lib/Audio_SdFat/effect_multiply.cpp +++ b/sampler/lib/Audio_SdFat/effect_multiply.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/effect_reverb.cpp b/sampler/lib/Audio_SdFat/effect_reverb.cpp index 58e5e09..e2cd1ae 100644 --- a/sampler/lib/Audio_SdFat/effect_reverb.cpp +++ b/sampler/lib/Audio_SdFat/effect_reverb.cpp @@ -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 diff --git a/sampler/lib/Audio_SdFat/examples/HardwareTesting/WM8731MikroSine/WM8731MikroSine.ino b/sampler/lib/Audio_SdFat/examples/HardwareTesting/WM8731MikroSine/WM8731MikroSine.ino index 5877f04..2781606 100644 --- a/sampler/lib/Audio_SdFat/examples/HardwareTesting/WM8731MikroSine/WM8731MikroSine.ino +++ b/sampler/lib/Audio_SdFat/examples/HardwareTesting/WM8731MikroSine/WM8731MikroSine.ino @@ -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 diff --git a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleCashregister.cpp b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleCashregister.cpp index 3f56c76..1780f49 100644 --- a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleCashregister.cpp +++ b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleCashregister.cpp @@ -1,8 +1,9 @@ // Audio data converted from WAV file by wav2sketch #include "AudioSampleCashregister.h" - +#include // 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, diff --git a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleGong.cpp b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleGong.cpp index 8fd15ca..eabd904 100644 --- a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleGong.cpp +++ b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleGong.cpp @@ -1,8 +1,9 @@ // Audio data converted from WAV file by wav2sketch #include "AudioSampleGong.h" - +#include // 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, diff --git a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleHihat.cpp b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleHihat.cpp index 3883f57..8242896 100644 --- a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleHihat.cpp +++ b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleHihat.cpp @@ -1,8 +1,9 @@ // Audio data converted from WAV file by wav2sketch #include "AudioSampleHihat.h" - +#include // 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, diff --git a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleKick.cpp b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleKick.cpp index 9cdee03..d4b8573 100644 --- a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleKick.cpp +++ b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleKick.cpp @@ -1,8 +1,9 @@ // Audio data converted from WAV file by wav2sketch #include "AudioSampleKick.h" - +#include // 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, diff --git a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleSnare.cpp b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleSnare.cpp index ae2b2d0..1c3f093 100644 --- a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleSnare.cpp +++ b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleSnare.cpp @@ -1,8 +1,9 @@ // Audio data converted from WAV file by wav2sketch #include "AudioSampleSnare.h" - +#include // 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, diff --git a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleTomtom.cpp b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleTomtom.cpp index 081e557..03ffdd2 100644 --- a/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleTomtom.cpp +++ b/sampler/lib/Audio_SdFat/examples/SamplePlayer/AudioSampleTomtom.cpp @@ -1,8 +1,9 @@ // Audio data converted from WAV file by wav2sketch #include "AudioSampleTomtom.h" - +#include // 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, diff --git a/sampler/lib/Audio_SdFat/examples/Synthesis/PlaySynthMusic/william_tell_overture.c b/sampler/lib/Audio_SdFat/examples/Synthesis/PlaySynthMusic/william_tell_overture.c index 405b14a..8c0dde0 100644 --- a/sampler/lib/Audio_SdFat/examples/Synthesis/PlaySynthMusic/william_tell_overture.c +++ b/sampler/lib/Audio_SdFat/examples/Synthesis/PlaySynthMusic/william_tell_overture.c @@ -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 -#else -#define PROGMEM -#endif +#include 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, diff --git a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/SimpleWavetable/Flute_100kbyte_samples.cpp b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/SimpleWavetable/Flute_100kbyte_samples.cpp index 135f8a6..3a24693 100644 --- a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/SimpleWavetable/Flute_100kbyte_samples.cpp +++ b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/SimpleWavetable/Flute_100kbyte_samples.cpp @@ -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 }; - diff --git a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/MutedTrumpet_samples.cpp b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/MutedTrumpet_samples.cpp index ecd556e..fb501d4 100644 --- a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/MutedTrumpet_samples.cpp +++ b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/MutedTrumpet_samples.cpp @@ -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 }; - diff --git a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Overworld.c b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Overworld.c index 073975e..cd95075 100644 --- a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Overworld.c +++ b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Overworld.c @@ -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 -#else -#define PROGMEM -#endif +#include + 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, diff --git a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Pizzicato_samples.cpp b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Pizzicato_samples.cpp index 01e789c..2ad3b7b 100644 --- a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Pizzicato_samples.cpp +++ b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Pizzicato_samples.cpp @@ -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, diff --git a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Viola_samples.cpp b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Viola_samples.cpp index 3166e32..571651d 100644 --- a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Viola_samples.cpp +++ b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/Viola_samples.cpp @@ -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, diff --git a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/WT_Trumpet_samples.cpp b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/WT_Trumpet_samples.cpp index 98e910e..3bee968 100644 --- a/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/WT_Trumpet_samples.cpp +++ b/sampler/lib/Audio_SdFat/examples/Synthesis/Wavetable/Zelda/WT_Trumpet_samples.cpp @@ -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 }; - diff --git a/sampler/lib/Audio_SdFat/extras/miditones/Makefile b/sampler/lib/Audio_SdFat/extras/miditones/Makefile new file mode 100755 index 0000000..089e9e3 --- /dev/null +++ b/sampler/lib/Audio_SdFat/extras/miditones/Makefile @@ -0,0 +1,5 @@ +miditones: miditones.c + gcc -O2 -Wall -o miditones miditones.c + +clean: + rm -f miditones diff --git a/sampler/lib/Audio_SdFat/extras/miditones/miditones.c b/sampler/lib/Audio_SdFat/extras/miditones/miditones.c new file mode 100644 index 0000000..deed642 --- /dev/null +++ b/sampler/lib/Audio_SdFat/extras/miditones/miditones.c @@ -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] +* +* The 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 .log file +* +* -lg Log output bytestream generation information to the .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 .bin, instead of a +* C-language source file with the name .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 +#include +#include +#include +#include +#include +#include + + +/*********** 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] ", + " -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: .mid", + "output file: .bin or .c", + "log file: .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 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; itrkptr[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; itrkptr[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<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<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 \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; +} diff --git a/sampler/lib/Audio_SdFat/extras/wav2sketch/wav2sketch.c b/sampler/lib/Audio_SdFat/extras/wav2sketch/wav2sketch.c new file mode 100644 index 0000000..fc1eca2 --- /dev/null +++ b/sampler/lib/Audio_SdFat/extras/wav2sketch/wav2sketch.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +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); +} + diff --git a/sampler/lib/Audio_SdFat/filter_biquad.cpp b/sampler/lib/Audio_SdFat/filter_biquad.cpp index a5c721d..a072ea0 100644 --- a/sampler/lib/Audio_SdFat/filter_biquad.cpp +++ b/sampler/lib/Audio_SdFat/filter_biquad.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/filter_variable.cpp b/sampler/lib/Audio_SdFat/filter_variable.cpp index b5b3634..d0a2df8 100644 --- a/sampler/lib/Audio_SdFat/filter_variable.cpp +++ b/sampler/lib/Audio_SdFat/filter_variable.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/input_adc.cpp b/sampler/lib/Audio_SdFat/input_adc.cpp index c5693bf..8d8cb3b 100644 --- a/sampler/lib/Audio_SdFat/input_adc.cpp +++ b/sampler/lib/Audio_SdFat/input_adc.cpp @@ -24,6 +24,8 @@ * THE SOFTWARE. */ +#if !defined(__IMXRT1052__) && !defined(__IMXRT1062__) + #include #include "input_adc.h" #include "utility/pdb.h" @@ -211,3 +213,4 @@ void AudioInputAnalog::update(void) transmit(out_left); release(out_left); } +#endif \ No newline at end of file diff --git a/sampler/lib/Audio_SdFat/input_i2s.cpp b/sampler/lib/Audio_SdFat/input_i2s.cpp index b3f9853..76f2966 100644 --- a/sampler/lib/Audio_SdFat/input_i2s.cpp +++ b/sampler/lib/Audio_SdFat/input_i2s.cpp @@ -24,11 +24,13 @@ * THE SOFTWARE. */ + + #include #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 + } - - - diff --git a/sampler/lib/Audio_SdFat/input_i2s2.cpp b/sampler/lib/Audio_SdFat/input_i2s2.cpp new file mode 100644 index 0000000..772249e --- /dev/null +++ b/sampler/lib/Audio_SdFat/input_i2s2.cpp @@ -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 +#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 diff --git a/sampler/lib/Audio_SdFat/input_i2s2.h b/sampler/lib/Audio_SdFat/input_i2s2.h new file mode 100644 index 0000000..7d90e3e --- /dev/null +++ b/sampler/lib/Audio_SdFat/input_i2s2.h @@ -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__) diff --git a/sampler/lib/Audio_SdFat/input_pdm.cpp b/sampler/lib/Audio_SdFat/input_pdm.cpp index 19b5c5a..976c417 100644 --- a/sampler/lib/Audio_SdFat/input_pdm.cpp +++ b/sampler/lib/Audio_SdFat/input_pdm.cpp @@ -24,6 +24,8 @@ * THE SOFTWARE. */ + #if !defined(__IMXRT1052__) && !defined(__IMXRT1062__) + #include #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 diff --git a/sampler/lib/Audio_SdFat/input_tdm.cpp b/sampler/lib/Audio_SdFat/input_tdm.cpp index 80e3620..77c5727 100644 --- a/sampler/lib/Audio_SdFat/input_tdm.cpp +++ b/sampler/lib/Audio_SdFat/input_tdm.cpp @@ -27,12 +27,14 @@ #include #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 diff --git a/sampler/lib/Audio_SdFat/input_tdm2.cpp b/sampler/lib/Audio_SdFat/input_tdm2.cpp new file mode 100644 index 0000000..ae3ebdc --- /dev/null +++ b/sampler/lib/Audio_SdFat/input_tdm2.cpp @@ -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 +#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 diff --git a/sampler/lib/Audio_SdFat/input_tdm2.h b/sampler/lib/Audio_SdFat/input_tdm2.h new file mode 100644 index 0000000..5daa526 --- /dev/null +++ b/sampler/lib/Audio_SdFat/input_tdm2.h @@ -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 \ No newline at end of file diff --git a/sampler/lib/Audio_SdFat/keywords.txt b/sampler/lib/Audio_SdFat/keywords.txt index 25e6895..7374f76 100644 --- a/sampler/lib/Audio_SdFat/keywords.txt +++ b/sampler/lib/Audio_SdFat/keywords.txt @@ -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 diff --git a/sampler/lib/Audio_SdFat/library.json b/sampler/lib/Audio_SdFat/library.json index 42a5bd1..c4756c1 100644 --- a/sampler/lib/Audio_SdFat/library.json +++ b/sampler/lib/Audio_SdFat/library.json @@ -1,5 +1,5 @@ { - "name": "Audio_SdFat", + "name": "Audio", "frameworks": "Arduino", "platforms": "Teensy", "keywords": "sound, audio, FFT, filter, effect", diff --git a/sampler/lib/Audio_SdFat/library.properties b/sampler/lib/Audio_SdFat/library.properties index 0f004ff..aa60d4f 100644 --- a/sampler/lib/Audio_SdFat/library.properties +++ b/sampler/lib/Audio_SdFat/library.properties @@ -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=* + diff --git a/sampler/lib/Audio_SdFat/memcpy_audio.S b/sampler/lib/Audio_SdFat/memcpy_audio.S index 4c4fe73..d8c8abe 100644 --- a/sampler/lib/Audio_SdFat/memcpy_audio.S +++ b/sampler/lib/Audio_SdFat/memcpy_audio.S @@ -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 .cpu cortex-m4 .syntax unified .thumb .text -.align 2 /* void memcpy_tointerleave(short *dst, short *srcL, short *srcR); */ .global memcpy_tointerleaveLR diff --git a/sampler/lib/Audio_SdFat/mixer.cpp b/sampler/lib/Audio_SdFat/mixer.cpp index 5379c37..7b56667 100644 --- a/sampler/lib/Audio_SdFat/mixer.cpp +++ b/sampler/lib/Audio_SdFat/mixer.cpp @@ -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) diff --git a/sampler/lib/Audio_SdFat/mixer.h b/sampler/lib/Audio_SdFat/mixer.h index 88cd37b..c3686c6 100644 --- a/sampler/lib/Audio_SdFat/mixer.h +++ b/sampler/lib/Audio_SdFat/mixer.h @@ -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; diff --git a/sampler/lib/Audio_SdFat/output_adat.cpp b/sampler/lib/Audio_SdFat/output_adat.cpp index 24d7fde..24c823d 100644 --- a/sampler/lib/Audio_SdFat/output_adat.cpp +++ b/sampler/lib/Audio_SdFat/output_adat.cpp @@ -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; - } - } -} diff --git a/sampler/lib/Audio_SdFat/output_i2s.cpp b/sampler/lib/Audio_SdFat/output_i2s.cpp index 19dd62d..9f369f6 100644 --- a/sampler/lib/Audio_SdFat/output_i2s.cpp +++ b/sampler/lib/Audio_SdFat/output_i2s.cpp @@ -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 } - - diff --git a/sampler/lib/Audio_SdFat/output_i2s2.cpp b/sampler/lib/Audio_SdFat/output_i2s2.cpp new file mode 100644 index 0000000..f605e12 --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_i2s2.cpp @@ -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 +#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__) diff --git a/sampler/lib/Audio_SdFat/output_i2s2.h b/sampler/lib/Audio_SdFat/output_i2s2.h new file mode 100644 index 0000000..0ada84f --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_i2s2.h @@ -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__) diff --git a/sampler/lib/Audio_SdFat/output_i2s_quad.cpp b/sampler/lib/Audio_SdFat/output_i2s_quad.cpp index b263b96..2cd973d 100644 --- a/sampler/lib/Audio_SdFat/output_i2s_quad.cpp +++ b/sampler/lib/Audio_SdFat/output_i2s_quad.cpp @@ -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 diff --git a/sampler/lib/Audio_SdFat/output_mqs.cpp b/sampler/lib/Audio_SdFat/output_mqs.cpp new file mode 100644 index 0000000..f9a3e7c --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_mqs.cpp @@ -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 +#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__) diff --git a/sampler/lib/Audio_SdFat/output_mqs.h b/sampler/lib/Audio_SdFat/output_mqs.h new file mode 100644 index 0000000..fa72b32 --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_mqs.h @@ -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__) diff --git a/sampler/lib/Audio_SdFat/output_pt8211.cpp b/sampler/lib/Audio_SdFat/output_pt8211.cpp index a8335fd..c3639b2 100644 --- a/sampler/lib/Audio_SdFat/output_pt8211.cpp +++ b/sampler/lib/Audio_SdFat/output_pt8211.cpp @@ -26,9 +26,11 @@ //Adapted to PT8211, Frank Bösing, Ben-Rheinland + #include #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 } diff --git a/sampler/lib/Audio_SdFat/output_pt8211.h b/sampler/lib/Audio_SdFat/output_pt8211.h index 2260121..7c5b699 100644 --- a/sampler/lib/Audio_SdFat/output_pt8211.h +++ b/sampler/lib/Audio_SdFat/output_pt8211.h @@ -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; diff --git a/sampler/lib/Audio_SdFat/output_pt8211_2.cpp b/sampler/lib/Audio_SdFat/output_pt8211_2.cpp new file mode 100644 index 0000000..c6bd7ef --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_pt8211_2.cpp @@ -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 +#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 diff --git a/sampler/lib/Audio_SdFat/output_pt8211_2.h b/sampler/lib/Audio_SdFat/output_pt8211_2.h new file mode 100644 index 0000000..2ed32c6 --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_pt8211_2.h @@ -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 diff --git a/sampler/lib/Audio_SdFat/output_spdif.cpp b/sampler/lib/Audio_SdFat/output_spdif.cpp index 66d063e..bd907c3 100644 --- a/sampler/lib/Audio_SdFat/output_spdif.cpp +++ b/sampler/lib/Audio_SdFat/output_spdif.cpp @@ -25,15 +25,7 @@ #include #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) { diff --git a/sampler/lib/Audio_SdFat/output_spdif.h b/sampler/lib/Audio_SdFat/output_spdif.h index 9adf96e..e982ea8 100644 --- a/sampler/lib/Audio_SdFat/output_spdif.h +++ b/sampler/lib/Audio_SdFat/output_spdif.h @@ -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; diff --git a/sampler/lib/Audio_SdFat/output_spdif2.cpp b/sampler/lib/Audio_SdFat/output_spdif2.cpp new file mode 100644 index 0000000..2e03392 --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_spdif2.cpp @@ -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 +#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 diff --git a/sampler/lib/Audio_SdFat/output_spdif2.h b/sampler/lib/Audio_SdFat/output_spdif2.h new file mode 100644 index 0000000..92b40b2 --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_spdif2.h @@ -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 diff --git a/sampler/lib/Audio_SdFat/output_spdif3.cpp b/sampler/lib/Audio_SdFat/output_spdif3.cpp new file mode 100644 index 0000000..900d98a --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_spdif3.cpp @@ -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 +#include "output_spdif3.h" +#include "utility/imxrt_hw.h" +#include "memcpy_audio.h" +#include + +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 diff --git a/sampler/lib/Audio_SdFat/output_spdif3.h b/sampler/lib/Audio_SdFat/output_spdif3.h new file mode 100644 index 0000000..8a03db4 --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_spdif3.h @@ -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 +#include +#include + +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 diff --git a/sampler/lib/Audio_SdFat/output_tdm.cpp b/sampler/lib/Audio_SdFat/output_tdm.cpp index dfa3f4d..b9f4551 100644 --- a/sampler/lib/Audio_SdFat/output_tdm.cpp +++ b/sampler/lib/Audio_SdFat/output_tdm.cpp @@ -27,27 +27,31 @@ #include #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 diff --git a/sampler/lib/Audio_SdFat/output_tdm2.cpp b/sampler/lib/Audio_SdFat/output_tdm2.cpp new file mode 100644 index 0000000..11a57fe --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_tdm2.cpp @@ -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 +#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 diff --git a/sampler/lib/Audio_SdFat/output_tdm2.h b/sampler/lib/Audio_SdFat/output_tdm2.h new file mode 100644 index 0000000..3f62771 --- /dev/null +++ b/sampler/lib/Audio_SdFat/output_tdm2.h @@ -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 \ No newline at end of file diff --git a/sampler/lib/Audio_SdFat/play_sd_raw.cpp b/sampler/lib/Audio_SdFat/play_sd_raw.cpp index 8714013..13487f1 100644 --- a/sampler/lib/Audio_SdFat/play_sd_raw.cpp +++ b/sampler/lib/Audio_SdFat/play_sd_raw.cpp @@ -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); diff --git a/sampler/lib/Audio_SdFat/play_sd_wav.cpp b/sampler/lib/Audio_SdFat/play_sd_wav.cpp index bd27359..7c8a8e4 100644 --- a/sampler/lib/Audio_SdFat/play_sd_wav.cpp +++ b/sampler/lib/Audio_SdFat/play_sd_wav.cpp @@ -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 ................ +*/ diff --git a/sampler/lib/Audio_SdFat/play_sd_wav.h b/sampler/lib/Audio_SdFat/play_sd_wav.h index 1d5f8b1..f852d69 100644 --- a/sampler/lib/Audio_SdFat/play_sd_wav.h +++ b/sampler/lib/Audio_SdFat/play_sd_wav.h @@ -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); diff --git a/sampler/lib/Audio_SdFat/synth_dc.h b/sampler/lib/Audio_SdFat/synth_dc.h index 7ff799b..77af75a 100644 --- a/sampler/lib/Audio_SdFat/synth_dc.h +++ b/sampler/lib/Audio_SdFat/synth_dc.h @@ -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) { diff --git a/sampler/lib/Audio_SdFat/synth_pwm.cpp b/sampler/lib/Audio_SdFat/synth_pwm.cpp index a3a8983..f1bb4ce 100644 --- a/sampler/lib/Audio_SdFat/synth_pwm.cpp +++ b/sampler/lib/Audio_SdFat/synth_pwm.cpp @@ -29,7 +29,7 @@ #include "utility/dspinst.h" -#if defined(KINETISK) +#if defined(__ARM_ARCH_7EM__) void AudioSynthWaveformPWM::update(void) { diff --git a/sampler/lib/Audio_SdFat/synth_sine.cpp b/sampler/lib/Audio_SdFat/synth_sine.cpp index d82d8e8..f95ef4e 100644 --- a/sampler/lib/Audio_SdFat/synth_sine.cpp +++ b/sampler/lib/Audio_SdFat/synth_sine.cpp @@ -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) { diff --git a/sampler/lib/Audio_SdFat/synth_waveform.cpp b/sampler/lib/Audio_SdFat/synth_waveform.cpp index e7d14c3..2bb7d17 100644 --- a/sampler/lib/Audio_SdFat/synth_waveform.cpp +++ b/sampler/lib/Audio_SdFat/synth_waveform.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/synth_whitenoise.cpp b/sampler/lib/Audio_SdFat/synth_whitenoise.cpp index b6607c4..797784b 100644 --- a/sampler/lib/Audio_SdFat/synth_whitenoise.cpp +++ b/sampler/lib/Audio_SdFat/synth_whitenoise.cpp @@ -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; diff --git a/sampler/lib/Audio_SdFat/utility/dspinst.h b/sampler/lib/Audio_SdFat/utility/dspinst.h index 50c9873..85615f1 100644 --- a/sampler/lib/Audio_SdFat/utility/dspinst.h +++ b/sampler/lib/Audio_SdFat/utility/dspinst.h @@ -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; diff --git a/sampler/lib/Audio_SdFat/utility/imxrt_hw.cpp b/sampler/lib/Audio_SdFat/utility/imxrt_hw.cpp new file mode 100644 index 0000000..5ad72ed --- /dev/null +++ b/sampler/lib/Audio_SdFat/utility/imxrt_hw.cpp @@ -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 diff --git a/sampler/lib/Audio_SdFat/utility/imxrt_hw.h b/sampler/lib/Audio_SdFat/utility/imxrt_hw.h new file mode 100644 index 0000000..f84c385 --- /dev/null +++ b/sampler/lib/Audio_SdFat/utility/imxrt_hw.h @@ -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 +#include + +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 + diff --git a/sampler/lib/Audio_SdFat/utility/pdb.h b/sampler/lib/Audio_SdFat/utility/pdb.h index 28c8226..7071d1a 100644 --- a/sampler/lib/Audio_SdFat/utility/pdb.h +++ b/sampler/lib/Audio_SdFat/utility/pdb.h @@ -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 \ No newline at end of file diff --git a/sampler/platformio.ini b/sampler/platformio.ini index 922609a..053881d 100644 --- a/sampler/platformio.ini +++ b/sampler/platformio.ini @@ -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}