upgrade Audio_SdFat & teensy-aruidno latest..
This commit is contained in:
parent
2fdc5dfe09
commit
bd39410b91
80 changed files with 5009 additions and 362 deletions
1
sampler/lib/Audio_SdFat/.gitignore
vendored
1
sampler/lib/Audio_SdFat/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
||||||
*.swp
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -91,18 +91,26 @@
|
||||||
#include "input_adc.h"
|
#include "input_adc.h"
|
||||||
#include "input_adcs.h"
|
#include "input_adcs.h"
|
||||||
#include "input_i2s.h"
|
#include "input_i2s.h"
|
||||||
|
#include "input_i2s2.h"
|
||||||
#include "input_i2s_quad.h"
|
#include "input_i2s_quad.h"
|
||||||
#include "input_tdm.h"
|
#include "input_tdm.h"
|
||||||
|
#include "input_tdm2.h"
|
||||||
#include "input_pdm.h"
|
#include "input_pdm.h"
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
#include "output_dac.h"
|
#include "output_dac.h"
|
||||||
#include "output_dacs.h"
|
#include "output_dacs.h"
|
||||||
#include "output_i2s.h"
|
#include "output_i2s.h"
|
||||||
|
#include "output_i2s2.h"
|
||||||
#include "output_i2s_quad.h"
|
#include "output_i2s_quad.h"
|
||||||
|
#include "output_mqs.h"
|
||||||
#include "output_pwm.h"
|
#include "output_pwm.h"
|
||||||
#include "output_spdif.h"
|
#include "output_spdif.h"
|
||||||
|
#include "output_spdif2.h"
|
||||||
|
#include "output_spdif3.h"
|
||||||
#include "output_pt8211.h"
|
#include "output_pt8211.h"
|
||||||
|
#include "output_pt8211_2.h"
|
||||||
#include "output_tdm.h"
|
#include "output_tdm.h"
|
||||||
|
#include "output_tdm2.h"
|
||||||
#include "output_adat.h"
|
#include "output_adat.h"
|
||||||
#include "play_memory.h"
|
#include "play_memory.h"
|
||||||
#include "play_queue.h"
|
#include "play_queue.h"
|
||||||
|
|
|
||||||
|
|
@ -62,7 +62,7 @@ void AudioAnalyzeFFT1024::update(void)
|
||||||
block = receiveReadOnly();
|
block = receiveReadOnly();
|
||||||
if (!block) return;
|
if (!block) return;
|
||||||
|
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 0:
|
case 0:
|
||||||
blocklist[0] = block;
|
blocklist[0] = block;
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ void AudioAnalyzeRMS::update(void)
|
||||||
count++;
|
count++;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
uint32_t *p = (uint32_t *)(block->data);
|
uint32_t *p = (uint32_t *)(block->data);
|
||||||
uint32_t *end = p + AUDIO_BLOCK_SAMPLES/2;
|
uint32_t *end = p + AUDIO_BLOCK_SAMPLES/2;
|
||||||
int64_t sum = accum;
|
int64_t sum = accum;
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
#include "analyze_tonedetect.h"
|
#include "analyze_tonedetect.h"
|
||||||
#include "utility/dspinst.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) __attribute__((always_inline));
|
||||||
static inline int32_t multiply_32x32_rshift30(int32_t a, int32_t b)
|
static inline int32_t multiply_32x32_rshift30(int32_t a, int32_t b)
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,11 @@
|
||||||
// then un-mute the DACs.
|
// then un-mute the DACs.
|
||||||
// 9. Normal operation begins.
|
// 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[] = {
|
static const uint8_t default_config[] = {
|
||||||
0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max
|
0xF4, // CS42448_Functional_Mode = slave mode, MCLK 25.6 MHz max
|
||||||
0x76, // CS42448_Interface_Formats = TDM mode
|
0x76, // CS42448_Interface_Formats = TDM mode
|
||||||
|
|
|
||||||
|
|
@ -519,7 +519,8 @@ bool AudioControlSGTL5000::enable(void)
|
||||||
//unsigned int n = read(CHIP_ID);
|
//unsigned int n = read(CHIP_ID);
|
||||||
//Serial.println(n, HEX);
|
//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_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_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
|
write(CHIP_LINE_OUT_CTRL, 0x0F22); // LO_VAGCNTRL=1.65V, OUT_CURRENT=0.54mA
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,11 @@ bool AudioControlWM8731::enable(void)
|
||||||
{
|
{
|
||||||
Wire.begin();
|
Wire.begin();
|
||||||
delay(5);
|
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_INTERFACE, 0x02); // I2S, 16 bit, MCLK slave
|
||||||
write(WM8731_REG_SAMPLING, 0x20); // 256*Fs, 44.1 kHz, MCLK/1
|
write(WM8731_REG_SAMPLING, 0x20); // 256*Fs, 44.1 kHz, MCLK/1
|
||||||
|
|
||||||
|
|
@ -74,15 +77,32 @@ bool AudioControlWM8731::enable(void)
|
||||||
return true;
|
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)
|
bool AudioControlWM8731::write(unsigned int reg, unsigned int val)
|
||||||
{
|
{
|
||||||
|
int attempt=0;
|
||||||
|
while (1) {
|
||||||
|
attempt++;
|
||||||
Wire.beginTransmission(WM8731_I2C_ADDR);
|
Wire.beginTransmission(WM8731_I2C_ADDR);
|
||||||
Wire.write((reg << 1) | ((val >> 8) & 1));
|
Wire.write((reg << 1) | ((val >> 8) & 1));
|
||||||
Wire.write(val & 0xFF);
|
Wire.write(val & 0xFF);
|
||||||
Wire.endTransmission();
|
int status = Wire.endTransmission();
|
||||||
|
if (status == 0) {
|
||||||
|
//Serial.printf("WM8731 write ok, %d tries\n", attempt);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (attempt >= 12) {
|
||||||
|
//Serial.printf("WM8731 write failed, %d tries\n", attempt);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
delayMicroseconds(80);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool AudioControlWM8731::volumeInteger(unsigned int n)
|
bool AudioControlWM8731::volumeInteger(unsigned int n)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
63
sampler/lib/Audio_SdFat/data_spdif.c
Normal file
63
sampler/lib/Audio_SdFat/data_spdif.c
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
const __attribute__((aligned(32)))
|
||||||
|
uint16_t spdif_bmclookup[256] = { //biphase mark encoded values (least significant bit first)
|
||||||
|
0xcccc, 0x4ccc, 0x2ccc, 0xaccc, 0x34cc, 0xb4cc, 0xd4cc, 0x54cc,
|
||||||
|
0x32cc, 0xb2cc, 0xd2cc, 0x52cc, 0xcacc, 0x4acc, 0x2acc, 0xaacc,
|
||||||
|
0x334c, 0xb34c, 0xd34c, 0x534c, 0xcb4c, 0x4b4c, 0x2b4c, 0xab4c,
|
||||||
|
0xcd4c, 0x4d4c, 0x2d4c, 0xad4c, 0x354c, 0xb54c, 0xd54c, 0x554c,
|
||||||
|
0x332c, 0xb32c, 0xd32c, 0x532c, 0xcb2c, 0x4b2c, 0x2b2c, 0xab2c,
|
||||||
|
0xcd2c, 0x4d2c, 0x2d2c, 0xad2c, 0x352c, 0xb52c, 0xd52c, 0x552c,
|
||||||
|
0xccac, 0x4cac, 0x2cac, 0xacac, 0x34ac, 0xb4ac, 0xd4ac, 0x54ac,
|
||||||
|
0x32ac, 0xb2ac, 0xd2ac, 0x52ac, 0xcaac, 0x4aac, 0x2aac, 0xaaac,
|
||||||
|
0x3334, 0xb334, 0xd334, 0x5334, 0xcb34, 0x4b34, 0x2b34, 0xab34,
|
||||||
|
0xcd34, 0x4d34, 0x2d34, 0xad34, 0x3534, 0xb534, 0xd534, 0x5534,
|
||||||
|
0xccb4, 0x4cb4, 0x2cb4, 0xacb4, 0x34b4, 0xb4b4, 0xd4b4, 0x54b4,
|
||||||
|
0x32b4, 0xb2b4, 0xd2b4, 0x52b4, 0xcab4, 0x4ab4, 0x2ab4, 0xaab4,
|
||||||
|
0xccd4, 0x4cd4, 0x2cd4, 0xacd4, 0x34d4, 0xb4d4, 0xd4d4, 0x54d4,
|
||||||
|
0x32d4, 0xb2d4, 0xd2d4, 0x52d4, 0xcad4, 0x4ad4, 0x2ad4, 0xaad4,
|
||||||
|
0x3354, 0xb354, 0xd354, 0x5354, 0xcb54, 0x4b54, 0x2b54, 0xab54,
|
||||||
|
0xcd54, 0x4d54, 0x2d54, 0xad54, 0x3554, 0xb554, 0xd554, 0x5554,
|
||||||
|
0x3332, 0xb332, 0xd332, 0x5332, 0xcb32, 0x4b32, 0x2b32, 0xab32,
|
||||||
|
0xcd32, 0x4d32, 0x2d32, 0xad32, 0x3532, 0xb532, 0xd532, 0x5532,
|
||||||
|
0xccb2, 0x4cb2, 0x2cb2, 0xacb2, 0x34b2, 0xb4b2, 0xd4b2, 0x54b2,
|
||||||
|
0x32b2, 0xb2b2, 0xd2b2, 0x52b2, 0xcab2, 0x4ab2, 0x2ab2, 0xaab2,
|
||||||
|
0xccd2, 0x4cd2, 0x2cd2, 0xacd2, 0x34d2, 0xb4d2, 0xd4d2, 0x54d2,
|
||||||
|
0x32d2, 0xb2d2, 0xd2d2, 0x52d2, 0xcad2, 0x4ad2, 0x2ad2, 0xaad2,
|
||||||
|
0x3352, 0xb352, 0xd352, 0x5352, 0xcb52, 0x4b52, 0x2b52, 0xab52,
|
||||||
|
0xcd52, 0x4d52, 0x2d52, 0xad52, 0x3552, 0xb552, 0xd552, 0x5552,
|
||||||
|
0xccca, 0x4cca, 0x2cca, 0xacca, 0x34ca, 0xb4ca, 0xd4ca, 0x54ca,
|
||||||
|
0x32ca, 0xb2ca, 0xd2ca, 0x52ca, 0xcaca, 0x4aca, 0x2aca, 0xaaca,
|
||||||
|
0x334a, 0xb34a, 0xd34a, 0x534a, 0xcb4a, 0x4b4a, 0x2b4a, 0xab4a,
|
||||||
|
0xcd4a, 0x4d4a, 0x2d4a, 0xad4a, 0x354a, 0xb54a, 0xd54a, 0x554a,
|
||||||
|
0x332a, 0xb32a, 0xd32a, 0x532a, 0xcb2a, 0x4b2a, 0x2b2a, 0xab2a,
|
||||||
|
0xcd2a, 0x4d2a, 0x2d2a, 0xad2a, 0x352a, 0xb52a, 0xd52a, 0x552a,
|
||||||
|
0xccaa, 0x4caa, 0x2caa, 0xacaa, 0x34aa, 0xb4aa, 0xd4aa, 0x54aa,
|
||||||
|
0x32aa, 0xb2aa, 0xd2aa, 0x52aa, 0xcaaa, 0x4aaa, 0x2aaa, 0xaaaa
|
||||||
|
};
|
||||||
|
|
@ -116,7 +116,7 @@ static const audio_block_t zeroblock = {
|
||||||
|
|
||||||
void AudioEffectFreeverb::update()
|
void AudioEffectFreeverb::update()
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
const audio_block_t *block;
|
const audio_block_t *block;
|
||||||
audio_block_t *outblock;
|
audio_block_t *outblock;
|
||||||
int i;
|
int i;
|
||||||
|
|
@ -294,7 +294,7 @@ AudioEffectFreeverbStereo::AudioEffectFreeverbStereo() : AudioStream(1, inputQue
|
||||||
|
|
||||||
void AudioEffectFreeverbStereo::update()
|
void AudioEffectFreeverbStereo::update()
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
const audio_block_t *block;
|
const audio_block_t *block;
|
||||||
audio_block_t *outblockL;
|
audio_block_t *outblockL;
|
||||||
audio_block_t *outblockR;
|
audio_block_t *outblockR;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ void AudioEffectMidSide::update(void)
|
||||||
if (blockb) release(blockb); // of the blocks is NULL then it's trouble anyway
|
if (blockb) release(blockb); // of the blocks is NULL then it's trouble anyway
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
pa = (uint32_t *)(blocka->data);
|
pa = (uint32_t *)(blocka->data);
|
||||||
pb = (uint32_t *)(blockb->data);
|
pb = (uint32_t *)(blockb->data);
|
||||||
end = pa + AUDIO_BLOCK_SAMPLES/2;
|
end = pa + AUDIO_BLOCK_SAMPLES/2;
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
void AudioEffectMultiply::update(void)
|
void AudioEffectMultiply::update(void)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
audio_block_t *blocka, *blockb;
|
audio_block_t *blocka, *blockb;
|
||||||
uint32_t *pa, *pb, *end;
|
uint32_t *pa, *pb, *end;
|
||||||
uint32_t a12, a34; //, a56, a78;
|
uint32_t a12, a34; //, a56, a78;
|
||||||
|
|
|
||||||
|
|
@ -137,14 +137,14 @@ AudioEffectReverb::init_comb_filters(void)
|
||||||
void
|
void
|
||||||
AudioEffectReverb::clear_buffers(void)
|
AudioEffectReverb::clear_buffers(void)
|
||||||
{
|
{
|
||||||
memset(apf1_buf, 0, APF1_BUF_LEN);
|
memset(apf1_buf, 0, sizeof(apf1_buf));
|
||||||
memset(apf2_buf, 0, APF1_BUF_LEN);
|
memset(apf2_buf, 0, sizeof(apf2_buf));
|
||||||
memset(apf3_buf, 0, APF1_BUF_LEN);
|
memset(apf3_buf, 0, sizeof(apf3_buf));
|
||||||
|
|
||||||
memset(lpf1_buf, 0, LPF1_BUF_LEN);
|
memset(lpf1_buf, 0, sizeof(lpf1_buf));
|
||||||
memset(lpf2_buf, 0, LPF2_BUF_LEN);
|
memset(lpf2_buf, 0, sizeof(lpf2_buf));
|
||||||
memset(lpf3_buf, 0, LPF3_BUF_LEN);
|
memset(lpf3_buf, 0, sizeof(lpf3_buf));
|
||||||
memset(lpf4_buf, 0, LPF4_BUF_LEN);
|
memset(lpf4_buf, 0, sizeof(lpf4_buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,10 @@
|
||||||
// 3.3V +3.3V
|
// 3.3V +3.3V
|
||||||
// GND GND
|
// 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.
|
// This example code is in the public domain.
|
||||||
|
|
||||||
#include <Audio.h>
|
#include <Audio.h>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Audio data converted from WAV file by wav2sketch
|
// Audio data converted from WAV file by wav2sketch
|
||||||
|
|
||||||
#include "AudioSampleCashregister.h"
|
#include "AudioSampleCashregister.h"
|
||||||
|
#include <Arduino.h>
|
||||||
// Converted from cashregister.wav, using 22050 Hz, u-law encoding
|
// Converted from cashregister.wav, using 22050 Hz, u-law encoding
|
||||||
|
PROGMEM
|
||||||
const unsigned int AudioSampleCashregister[5809] = {
|
const unsigned int AudioSampleCashregister[5809] = {
|
||||||
0x02005AB4,0x82060707,0x03010301,0x08038287,0x81820200,0x09120407,0x15091108,0x02080611,
|
0x02005AB4,0x82060707,0x03010301,0x08038287,0x81820200,0x09120407,0x15091108,0x02080611,
|
||||||
0x0D050D11,0x8008150C,0x93810480,0x000D8890,0x0A060406,0x06000681,0x80828702,0x89928405,
|
0x0D050D11,0x8008150C,0x93810480,0x000D8890,0x0A060406,0x06000681,0x80828702,0x89928405,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Audio data converted from WAV file by wav2sketch
|
// Audio data converted from WAV file by wav2sketch
|
||||||
|
|
||||||
#include "AudioSampleGong.h"
|
#include "AudioSampleGong.h"
|
||||||
|
#include <Arduino.h>
|
||||||
// Converted from gong.wav, using 11025 Hz, u-law encoding
|
// Converted from gong.wav, using 11025 Hz, u-law encoding
|
||||||
|
PROGMEM
|
||||||
const unsigned int AudioSampleGong[27633] = {
|
const unsigned int AudioSampleGong[27633] = {
|
||||||
0x0301AFA4,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
0x0301AFA4,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
0x00000000,0x00000000,0x00000000,0x00000000,0x00008000,0x00000000,0x00000000,0x80800000,
|
0x00000000,0x00000000,0x00000000,0x00000000,0x00008000,0x00000000,0x00000000,0x80800000,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Audio data converted from WAV file by wav2sketch
|
// Audio data converted from WAV file by wav2sketch
|
||||||
|
|
||||||
#include "AudioSampleHihat.h"
|
#include "AudioSampleHihat.h"
|
||||||
|
#include <Arduino.h>
|
||||||
// Converted from hihat.wav, using 44100 Hz, u-law encoding
|
// Converted from hihat.wav, using 44100 Hz, u-law encoding
|
||||||
|
PROGMEM
|
||||||
const unsigned int AudioSampleHihat[5953] = {
|
const unsigned int AudioSampleHihat[5953] = {
|
||||||
0x01005CB1,0x77766877,0xEAEC2195,0xE0737099,0x82807B70,0x88909012,0x8B822303,0x8C04180B,
|
0x01005CB1,0x77766877,0xEAEC2195,0xE0737099,0x82807B70,0x88909012,0x8B822303,0x8C04180B,
|
||||||
0x090B9207,0x878EA413,0x1EB09F15,0x0B9B2122,0xA09E0094,0x51B2B980,0xD1485CCA,0xDACCBF48,
|
0x090B9207,0x878EA413,0x1EB09F15,0x0B9B2122,0xA09E0094,0x51B2B980,0xD1485CCA,0xDACCBF48,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Audio data converted from WAV file by wav2sketch
|
// Audio data converted from WAV file by wav2sketch
|
||||||
|
|
||||||
#include "AudioSampleKick.h"
|
#include "AudioSampleKick.h"
|
||||||
|
#include <Arduino.h>
|
||||||
// Converted from kick.wav, using 22050 Hz, 16 bit PCM encoding
|
// Converted from kick.wav, using 22050 Hz, 16 bit PCM encoding
|
||||||
|
PROGMEM
|
||||||
const unsigned int AudioSampleKick[2561] = {
|
const unsigned int AudioSampleKick[2561] = {
|
||||||
0x820013EC,0xFFDC0027,0xFF710095,0x038DFF4C,0xFBA10105,0x0037FB6E,0x09D2011A,0x007504CA,
|
0x820013EC,0xFFDC0027,0xFF710095,0x038DFF4C,0xFBA10105,0x0037FB6E,0x09D2011A,0x007504CA,
|
||||||
0x024BF6BF,0xF77B0AFF,0xFB72F723,0xEF3DEBAD,0x0F7009D6,0x09DD1736,0xF26EFA3E,0x0D7002FB,
|
0x024BF6BF,0xF77B0AFF,0xFB72F723,0xEF3DEBAD,0x0F7009D6,0x09DD1736,0xF26EFA3E,0x0D7002FB,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Audio data converted from WAV file by wav2sketch
|
// Audio data converted from WAV file by wav2sketch
|
||||||
|
|
||||||
#include "AudioSampleSnare.h"
|
#include "AudioSampleSnare.h"
|
||||||
|
#include <Arduino.h>
|
||||||
// Converted from snare.wav, using 22050 Hz, u-law encoding
|
// Converted from snare.wav, using 22050 Hz, u-law encoding
|
||||||
|
PROGMEM
|
||||||
const unsigned int AudioSampleSnare[2817] = {
|
const unsigned int AudioSampleSnare[2817] = {
|
||||||
0x02002BD3,0x65636656,0x6B6A6B67,0x7071706F,0x43637171,0x29ABBA23,0x3137474C,0x3A4A544D,
|
0x02002BD3,0x65636656,0x6B6A6B67,0x7071706F,0x43637171,0x29ABBA23,0x3137474C,0x3A4A544D,
|
||||||
0x30C1542C,0xE14F6360,0xEDDCE2E6,0xEEF1F4F4,0xEEEDEAEA,0x3745A2E2,0xE7E6DAC8,0xC7C3C3DA,
|
0x30C1542C,0xE14F6360,0xEDDCE2E6,0xEEF1F4F4,0xEEEDEAEA,0x3745A2E2,0xE7E6DAC8,0xC7C3C3DA,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
// Audio data converted from WAV file by wav2sketch
|
// Audio data converted from WAV file by wav2sketch
|
||||||
|
|
||||||
#include "AudioSampleTomtom.h"
|
#include "AudioSampleTomtom.h"
|
||||||
|
#include <Arduino.h>
|
||||||
// Converted from tomtom.wav, using 11025 Hz, 16 bit PCM encoding
|
// Converted from tomtom.wav, using 11025 Hz, 16 bit PCM encoding
|
||||||
|
PROGMEM
|
||||||
const unsigned int AudioSampleTomtom[3489] = {
|
const unsigned int AudioSampleTomtom[3489] = {
|
||||||
0x83001B3C,0xFC0EFF8B,0xFB40FD97,0x045BFB30,0xEABCEAD6,0x04C2F8B3,0x002B0733,0xFB1C0879,
|
0x83001B3C,0xFC0EFF8B,0xFB40FD97,0x045BFB30,0xEABCEAD6,0x04C2F8B3,0x002B0733,0xFB1C0879,
|
||||||
0xE9CCED7A,0xEC2AEC4D,0xF7D8EF09,0xF6EC0EE6,0x15A4F3A6,0xFAD81B95,0xF591F001,0x0A3FFF14,
|
0xE9CCED7A,0xEC2AEC4D,0xF7D8EF09,0xF6EC0EE6,0x15A4F3A6,0xFAD81B95,0xF591F001,0x0A3FFF14,
|
||||||
|
|
|
||||||
|
|
@ -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
|
// 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
|
// command line: ./miditones -t16 -v william_tell_overture
|
||||||
#ifdef __AVR__
|
#include <Arduino.h>
|
||||||
#include <avr/pgmspace.h>
|
|
||||||
#else
|
|
||||||
#define PROGMEM
|
|
||||||
#endif
|
|
||||||
const unsigned char PROGMEM score [] = {
|
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,
|
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,
|
0x84, 0,183, 0x90,72,127, 0x91,72,107, 0x92,72,127, 0x93,72,127, 0x94,72,127, 0,41, 0x80,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "Flute_100kbyte_samples.h"
|
#include "Flute_100kbyte_samples.h"
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
static const uint32_t sample_0_Flute_100kbyte_FluteD4[7936] = {
|
static const uint32_t sample_0_Flute_100kbyte_FluteD4[7936] = {
|
||||||
0xfbfffc4b,0xfba4fbac,0xfbbffbb2,0xfc00fbdc,0xfc3afc16,0xfcb8fc57,0xfcf9fcf3,0xfc87fcad,
|
0xfbfffc4b,0xfba4fbac,0xfbbffbb2,0xfc00fbdc,0xfc3afc16,0xfcb8fc57,0xfcf9fcf3,0xfc87fcad,
|
||||||
0xfcabfc77,0xfd8cfd0c,0xfe7dfe0c,0xff24fee0,0xffc4ff61,0x006a002d,0x002d004c,0xffe90000,
|
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,
|
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
static const uint32_t sample_1_Flute_100kbyte_FluteE5[9472] = {
|
static const uint32_t sample_1_Flute_100kbyte_FluteE5[9472] = {
|
||||||
0x03f00410,0x03f003c0,0x04500410,0x04500460,0x04200410,0x03800400,0x02f00320,0x01900230,
|
0x03f00410,0x03f003c0,0x04500410,0x04500460,0x04200410,0x03800400,0x02f00320,0x01900230,
|
||||||
0x005000c0,0xff30ffb0,0xff70ff40,0xfe10fe90,0xfd70fde0,0xfc70fcc0,0xfc30fc00,0xfc40fca0,
|
0x005000c0,0xff30ffb0,0xff70ff40,0xfe10fe90,0xfd70fde0,0xfc70fcc0,0xfc30fc00,0xfc40fca0,
|
||||||
|
|
@ -2169,6 +2171,7 @@ static const uint32_t sample_1_Flute_100kbyte_FluteE5[9472] = {
|
||||||
0x00000000,0x00000000,
|
0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
static const uint32_t sample_2_Flute_100kbyte_FluteE6[7936] = {
|
static const uint32_t sample_2_Flute_100kbyte_FluteE6[7936] = {
|
||||||
0x01b003e0,0x02e00250,0x01300150,0x003000f0,0x01400180,0xffc0fed0,0xff600100,0x0080ffc0,
|
0x01b003e0,0x02e00250,0x01300150,0x003000f0,0x01400180,0xffc0fed0,0xff600100,0x0080ffc0,
|
||||||
0xffd0feb0,0xff500130,0x0070feb0,0xfed00060,0x00d0ffc0,0xff10ff50,0xff500030,0x0080ff60,
|
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, };
|
static const uint8_t Flute_100kbyte_ranges[] = {68, 83, 127, };
|
||||||
|
|
||||||
const AudioSynthWavetable::instrument_data Flute_100kbyte = {3, Flute_100kbyte_ranges, Flute_100kbyte_samples };
|
const AudioSynthWavetable::instrument_data Flute_100kbyte = {3, Flute_100kbyte_ranges, Flute_100kbyte_samples };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "MutedTrumpet_samples.h"
|
#include "MutedTrumpet_samples.h"
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
static const uint32_t sample_0_MutedTrumpet_HRMMUTED4[8192] = {
|
static const uint32_t sample_0_MutedTrumpet_HRMMUTED4[8192] = {
|
||||||
0xffdaffd4,0xffdeffdd,0xffe4ffe1,0xffe2ffe5,0xffd0ffda,0xffbfffc7,0xffb8ffb9,0xffb8ffb9,
|
0xffdaffd4,0xffdeffdd,0xffe4ffe1,0xffe2ffe5,0xffd0ffda,0xffbfffc7,0xffb8ffb9,0xffb8ffb9,
|
||||||
0xffb1ffb3,0xffaeffb1,0xffabffaa,0xffb1ffaf,0xffacffb0,0xffa2ffa7,0xffa3ffa0,0xffa9ffa7,
|
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,
|
0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
static const uint32_t sample_1_MutedTrumpet_HRMMUTEC5[6912] = {
|
static const uint32_t sample_1_MutedTrumpet_HRMMUTEC5[6912] = {
|
||||||
0x00000000,0xfffeffff,0x0000ffff,0xffff0000,0xfffffffe,0xffffffff,0xfffdfffe,0xfffdfffd,
|
0x00000000,0xfffeffff,0x0000ffff,0xffff0000,0xfffffffe,0xffffffff,0xfffdfffe,0xfffdfffd,
|
||||||
0xfffbfffc,0xfff9fff9,0xfffbfffa,0xfffbfffb,0xfffafffb,0xfffafff9,0xfffbfffb,0xfff8fff9,
|
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,
|
0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
static const uint32_t sample_2_MutedTrumpet_HRMMUTEC6[4608] = {
|
static const uint32_t sample_2_MutedTrumpet_HRMMUTEC6[4608] = {
|
||||||
0xff89ff87,0xff85ff8a,0xff7cff7f,0xff7bff7b,0xff79ff7a,0xff74ff77,0xff73ff72,0xff7cff78,
|
0xff89ff87,0xff85ff8a,0xff7cff7f,0xff7bff7b,0xff79ff7a,0xff74ff77,0xff73ff72,0xff7cff78,
|
||||||
0xff82ff80,0xff85ff83,0xff8bff88,0xff8eff8d,0xff90ff90,0xff8cff8f,0xff87ff89,0xff7eff83,
|
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, };
|
static const uint8_t MutedTrumpet_ranges[] = {71, 82, 127, };
|
||||||
|
|
||||||
const AudioSynthWavetable::instrument_data MutedTrumpet = {3, MutedTrumpet_ranges, MutedTrumpet_samples };
|
const AudioSynthWavetable::instrument_data MutedTrumpet = {3, MutedTrumpet_ranges, MutedTrumpet_samples };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
// 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
|
#include <Arduino.h>
|
||||||
#ifdef __AVR__
|
|
||||||
#include <avr/pgmspace.h>
|
|
||||||
#else
|
|
||||||
#define PROGMEM
|
|
||||||
#endif
|
|
||||||
const unsigned char PROGMEM score [] = {
|
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,
|
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,
|
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,
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,7 @@ const AudioSynthWavetable::sample_data Pizzicato_samples[4] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_0_Pizzicato_PizzViolinE3[2944] = {
|
const uint32_t sample_0_Pizzicato_PizzViolinE3[2944] = {
|
||||||
0xffeeffbc,0x03230104,0x062004f3,0x0d4f07cb,0x1c351736,0x16ae18bd,0x1cad1a19,0x1ed81ede,
|
0xffeeffbc,0x03230104,0x062004f3,0x0d4f07cb,0x1c351736,0x16ae18bd,0x1cad1a19,0x1ed81ede,
|
||||||
0x1a1b1b79,0x1ea61c1e,0x11081a2f,0x074a0a00,0x034305c7,0x012e02b7,0xf781fc58,0xf5d7f770,
|
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,
|
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_1_Pizzicato_PizzViolinC4[2304] = {
|
const uint32_t sample_1_Pizzicato_PizzViolinC4[2304] = {
|
||||||
0x02770000,0x0b55063e,0x109c0f35,0x0d690fda,0x088109dd,0xfea10525,0xea5bf55e,0xec27e61c,
|
0x02770000,0x0b55063e,0x109c0f35,0x0d690fda,0x088109dd,0xfea10525,0xea5bf55e,0xec27e61c,
|
||||||
0xf843f547,0xebc1f337,0xeb4be81f,0xf9dcf398,0xf90ffa32,0xfe29faae,0x043a0215,0xf9d20247,
|
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] = {
|
const uint32_t sample_2_Pizzicato_PizzViolinE5[768] = {
|
||||||
0x00330000,0x0c1e0957,0x100c0a13,0xefb7085e,0xef13e447,0x25e11a69,0x23032238,0xc02ed183,
|
0x00330000,0x0c1e0957,0x100c0a13,0xefb7085e,0xef13e447,0x25e11a69,0x23032238,0xc02ed183,
|
||||||
0xe93ddc07,0x0e2f1cb4,0x0a35fcfa,0xe333f478,0xee61d669,0x14351329,0x34ef33f1,0xd8cee3ff,
|
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,
|
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_3_Pizzicato_PizzViolinE5[768] = {
|
const uint32_t sample_3_Pizzicato_PizzViolinE5[768] = {
|
||||||
0x00330000,0x0c1e0957,0x100c0a13,0xefb7085e,0xef13e447,0x25e11a69,0x23032238,0xc02ed183,
|
0x00330000,0x0c1e0957,0x100c0a13,0xefb7085e,0xef13e447,0x25e11a69,0x23032238,0xc02ed183,
|
||||||
0xe93ddc07,0x0e2f1cb4,0x0a35fcfa,0xe333f478,0xee61d669,0x14351329,0x34ef33f1,0xd8cee3ff,
|
0xe93ddc07,0x0e2f1cb4,0x0a35fcfa,0xe333f478,0xee61d669,0x14351329,0x34ef33f1,0xd8cee3ff,
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,7 @@ const AudioSynthWavetable::sample_data Viola_samples[8] = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_0_Viola_ViolinBb2[768] = {
|
const uint32_t sample_0_Viola_ViolinBb2[768] = {
|
||||||
0x00c00062,0x01d00177,0x00d901a3,0xfd26ff1b,0xfbfffc14,0xfcc2fc94,0xfbf4fc33,0xfdbcfca6,
|
0x00c00062,0x01d00177,0x00d901a3,0xfd26ff1b,0xfbfffc14,0xfcc2fc94,0xfbf4fc33,0xfdbcfca6,
|
||||||
0xfe03fe3f,0xfe96fddc,0x035100ee,0x035d0423,0x002c01be,0x00e00025,0x002f0108,0xff46ff54,
|
0xfe03fe3f,0xfe96fddc,0x035100ee,0x035d0423,0x002c01be,0x00e00025,0x002f0108,0xff46ff54,
|
||||||
|
|
@ -299,6 +300,7 @@ const uint32_t sample_0_Viola_ViolinBb2[768] = {
|
||||||
0x00000000,0x00000000,
|
0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_1_Viola_ViolinD3[896] = {
|
const uint32_t sample_1_Viola_ViolinD3[896] = {
|
||||||
0xf22af42c,0xf477f50d,0x02bbf927,0x0e000a05,0x143e1088,0x15ac15b8,0x11f914ba,0x12870f75,
|
0xf22af42c,0xf477f50d,0x02bbf927,0x0e000a05,0x143e1088,0x15ac15b8,0x11f914ba,0x12870f75,
|
||||||
0x17fa1743,0x0d0c1332,0x07180989,0xfdef036b,0xfc75f893,0x0a50048f,0x0761093c,0x0e180a9b,
|
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,
|
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_2_Viola_ViolinG3[768] = {
|
const uint32_t sample_2_Viola_ViolinG3[768] = {
|
||||||
0xff4afeb7,0xfeb0fed3,0xff55ff4a,0xffbeff92,0xffd9fff0,0x000f0001,0xfff0000b,0x00750031,
|
0xff4afeb7,0xfeb0fed3,0xff55ff4a,0xffbeff92,0xffd9fff0,0x000f0001,0xfff0000b,0x00750031,
|
||||||
0x00be00e0,0x017f009a,0x020e01c7,0x025d024c,0x031002b9,0x036e02ec,0x038e0306,0x03420271,
|
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,
|
0xed98fa3f,0x0000dd29,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_3_Viola_ViolinC4[768] = {
|
const uint32_t sample_3_Viola_ViolinC4[768] = {
|
||||||
0x02720224,0x01790179,0x015b010f,0x023001a2,0x02dc0298,0x01d50280,0x00970141,0x011300f4,
|
0x02720224,0x01790179,0x015b010f,0x023001a2,0x02dc0298,0x01d50280,0x00970141,0x011300f4,
|
||||||
0x00be00f3,0xffe3002b,0xffbdffdf,0x00560035,0x007900af,0xffd30046,0xff02fef0,0xfed0feb6,
|
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] = {
|
const uint32_t sample_4_Viola_ViolinGb4[768] = {
|
||||||
0x0014001b,0x00150027,0x00390028,0x00450027,0x00120028,0x001b0022,0xffecfffe,0xffe6ffe0,
|
0x0014001b,0x00150027,0x00390028,0x00450027,0x00120028,0x001b0022,0xffecfffe,0xffe6ffe0,
|
||||||
0xffeefffd,0x00370014,0x0093fff8,0xff980066,0x02d90135,0xf8f6052d,0xf60cf32b,0xfefdf844,
|
0xffeefffd,0x00370014,0x0093fff8,0xff980066,0x02d90135,0xf8f6052d,0xf60cf32b,0xfefdf844,
|
||||||
|
|
@ -685,6 +690,7 @@ const uint32_t sample_4_Viola_ViolinGb4[768] = {
|
||||||
0x00000000,0x00000000,0x00000000,0x00000000,
|
0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_5_Viola_ViolinC5[640] = {
|
const uint32_t sample_5_Viola_ViolinC5[640] = {
|
||||||
0x01eafdb7,0xf3c6fadb,0xf8bef4e7,0x0c1aff72,0x15b51664,0x12ce16b5,0x0f101140,0x0b890d0a,
|
0x01eafdb7,0xf3c6fadb,0xf8bef4e7,0x0c1aff72,0x15b51664,0x12ce16b5,0x0f101140,0x0b890d0a,
|
||||||
0x0aaa0be0,0x1905125c,0x2b9824a1,0x14a51f92,0xe92efcc9,0xe20ae403,0xde50e318,0xd902def2,
|
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,
|
0x1659239f,0x13282024,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_6_Viola_ViolinEb5[768] = {
|
const uint32_t sample_6_Viola_ViolinEb5[768] = {
|
||||||
0xff93feb8,0xfde4fb64,0xfe2dfc67,0x12fafe5e,0x120d13e5,0x00ca05cc,0x076307c9,0x0f5f06b9,
|
0xff93feb8,0xfde4fb64,0xfe2dfc67,0x12fafe5e,0x120d13e5,0x00ca05cc,0x076307c9,0x0f5f06b9,
|
||||||
0x08080e4a,0x0f820d05,0x1c4813ad,0x0f4413f8,0xf9d10825,0xef44ec7d,0xeda5ec23,0xe7b9ed75,
|
0x08080e4a,0x0f820d05,0x1c4813ad,0x0f4413f8,0xf9d10825,0xef44ec7d,0xeda5ec23,0xe7b9ed75,
|
||||||
|
|
@ -863,6 +870,7 @@ const uint32_t sample_6_Viola_ViolinEb5[768] = {
|
||||||
0x00000000,0x00000000,
|
0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
const uint32_t sample_7_Viola_ViolinEb6[512] = {
|
const uint32_t sample_7_Viola_ViolinEb6[512] = {
|
||||||
0xfde7ff2e,0xffebfcf6,0x09180f77,0x066b0250,0x07fe09b7,0x12670b88,0xfd950d9b,0xf116f10d,
|
0xfde7ff2e,0xffebfcf6,0x09180f77,0x066b0250,0x07fe09b7,0x12670b88,0xfd950d9b,0xf116f10d,
|
||||||
0xe96aef60,0xf60fe23f,0x129212e1,0xf943fe40,0xf013f48b,0xf7b2f480,0x000afda7,0x0d25014e,
|
0xe96aef60,0xf60fe23f,0x129212e1,0xf943fe40,0xf013f48b,0xf7b2f480,0x000afda7,0x0d25014e,
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "WT_Trumpet_samples.h"
|
#include "WT_Trumpet_samples.h"
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
static const uint32_t sample_0_WT_Trumpet_trum04[10112] = {
|
static const uint32_t sample_0_WT_Trumpet_trum04[10112] = {
|
||||||
0xff9cff8f,0xffceffa9,0xfff90000,0xffd7ffe9,0xffbcffb2,0xff72ffa4,0xffc4ff9e,0xffbcffb2,
|
0xff9cff8f,0xffceffa9,0xfff90000,0xffd7ffe9,0xffbcffb2,0xff72ffa4,0xffc4ff9e,0xffbcffb2,
|
||||||
0xffdcffca,0x006a001e,0x012600c2,0x01620159,0x01430152,0x014e014d,0x0118013c,0x00b100e5,
|
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,
|
0x07f2f8b2,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
static const uint32_t sample_1_WT_Trumpet_trum09[9728] = {
|
static const uint32_t sample_1_WT_Trumpet_trum09[9728] = {
|
||||||
0x0f1f0a8f,0x15811318,0x0d67137e,0xfeec05c4,0xf883fa6c,0xf8c9f836,0xfb84fa14,0xfe46fceb,
|
0x0f1f0a8f,0x15811318,0x0d67137e,0xfeec05c4,0xf883fa6c,0xf8c9f836,0xfb84fa14,0xfe46fceb,
|
||||||
0xfeb1fee5,0xfe30fe62,0xfd05fdc9,0xfc17fc35,0xfbcafc52,0xf9c7fac5,0xf77af88a,0xf6e0f6d9,
|
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, };
|
static const uint8_t WT_Trumpet_ranges[] = {70, 127, };
|
||||||
|
|
||||||
const AudioSynthWavetable::instrument_data WT_Trumpet = {2, WT_Trumpet_ranges, WT_Trumpet_samples };
|
const AudioSynthWavetable::instrument_data WT_Trumpet = {2, WT_Trumpet_ranges, WT_Trumpet_samples };
|
||||||
|
|
||||||
|
|
|
||||||
5
sampler/lib/Audio_SdFat/extras/miditones/Makefile
Executable file
5
sampler/lib/Audio_SdFat/extras/miditones/Makefile
Executable file
|
|
@ -0,0 +1,5 @@
|
||||||
|
miditones: miditones.c
|
||||||
|
gcc -O2 -Wall -o miditones miditones.c
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f miditones
|
||||||
991
sampler/lib/Audio_SdFat/extras/miditones/miditones.c
Normal file
991
sampler/lib/Audio_SdFat/extras/miditones/miditones.c
Normal file
|
|
@ -0,0 +1,991 @@
|
||||||
|
/*********************************************************************************
|
||||||
|
*
|
||||||
|
* MIDITONES
|
||||||
|
*
|
||||||
|
* Convert a MIDI file into a bytestream of notes
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* (C) Copyright 2011,2013,2015,2016 Len Shustek
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 3 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation at http://www.gnu.org/licenses,
|
||||||
|
* with Additional Permissions under term 7(b) that the original copyright
|
||||||
|
* notice and author attibution must be preserved and under term 7(c) that
|
||||||
|
* modified versions be marked as different from the original.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
***********************************************************************************/
|
||||||
|
/*
|
||||||
|
* Change log
|
||||||
|
* 19 January 2011, L.Shustek, V1.0
|
||||||
|
* -Initial release.
|
||||||
|
* 26 February 2011, L. Shustek, V1.1
|
||||||
|
* -Expand the documentation generated in the output file.
|
||||||
|
* -End the binary output file with an "end of score" command.
|
||||||
|
* -Fix bug: Some "stop note" commands were generated too early.
|
||||||
|
* 04 March 2011, L. Shustek, V1.2
|
||||||
|
* -Minor error message rewording.
|
||||||
|
* 13 June 2011, L. Shustek, V1.3
|
||||||
|
* -Add -s2 strategy to try to keep each track on its own tone generator
|
||||||
|
* for when there are separate speakers. This obviously works only when
|
||||||
|
* each track is monophonic. (Suggested by Michal Pustejovsky)
|
||||||
|
* 20 November 2011, L. Shustek, V1.4
|
||||||
|
* -Add -cn option to mask which channels (tracks) to process
|
||||||
|
* -Add -kn option to change key
|
||||||
|
* Both of these are in support of music-playing on my Tesla Coil.
|
||||||
|
* 05 December 2011, L. Shustek, V1.5
|
||||||
|
* -Fix command line parsing error for option -s1
|
||||||
|
* -Display the commandline in the C file output
|
||||||
|
* -Change to decimal instead of hex for note numbers in the C file output
|
||||||
|
* 06 August 2013, L. Shustek, V1.6
|
||||||
|
* -Changed to allow compilation and execution in 64-bit environments
|
||||||
|
* by using C99 standard intN_t and uintN_t types for MIDI structures,
|
||||||
|
* and formatting specifications like "PRId32" instead of "ld".
|
||||||
|
* 04 April 2015, L. Shustek, V1.7
|
||||||
|
* -Made friendlier to other compilers: import source of strlcpy and strlcat,
|
||||||
|
* fixed various type mismatches that the LCC compiler didn't fret about.
|
||||||
|
* Generate "const" for data initialization for compatibility with Arduino IDE v1.6.x.
|
||||||
|
* 23 January 2016, D. Blackketter, V1.8
|
||||||
|
* -Fix warnings and errors building on Mac OS X via "gcc miditones.c"
|
||||||
|
* 25 January 2016, D. Blackketter, Paul Stoffregen, V1.9
|
||||||
|
-Merge in velocity output option from Arduino/Teensy Audio Library
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define VERSION "1.9"
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* About MIDITONES
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* MIDITONES converts a MIDI music file into a much simplified stream of commands,
|
||||||
|
* so that a version of the music can be played on a synthesizer having only
|
||||||
|
* tone generators without any volume or tone controls.
|
||||||
|
*
|
||||||
|
* Volume ("velocity") and instrument specifications in the MIDI files are discarded.
|
||||||
|
* All the tracks are prcoessed and merged into a single time-ordered stream of
|
||||||
|
* "note on", "note off", and "delay" commands.
|
||||||
|
*
|
||||||
|
* This was written for the "Playtune" Arduino library, which plays polyphonic music
|
||||||
|
* using up to 6 tone generators run by the timers on the processor. See the separate
|
||||||
|
* documentation for Playtune. But MIDITONES may prove useful for other tone
|
||||||
|
* generating systems.
|
||||||
|
*
|
||||||
|
* The output can be either a C-language source code fragment that initializes an
|
||||||
|
* array with the command bytestream, or a binary file with the bytestream itself.
|
||||||
|
*
|
||||||
|
* MIDITONES is written in standard ANSI C (plus strlcpy and strlcat functions), and
|
||||||
|
* is meant to be executed from the command line. There is no GUI interface.
|
||||||
|
*
|
||||||
|
* The MIDI file format is complicated, and this has not been tested on a very
|
||||||
|
* wide variety of file types. In particular, we have tested only format type "1",
|
||||||
|
* which seems to be what most of them are. Let me know if you find MIDI files
|
||||||
|
* that it won't digest and I'll see if I can fix it.
|
||||||
|
|
||||||
|
* This has been tested only on a little-endian PC, but I think it should work on
|
||||||
|
* big-endian processors too. Note that the MIDI file format is inherently
|
||||||
|
* big-endian.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ***** The command line *****
|
||||||
|
*
|
||||||
|
* To convert a MIDI file called "chopin.mid" into a command bytestream, execute
|
||||||
|
*
|
||||||
|
* miditones chopin
|
||||||
|
*
|
||||||
|
* It will create a file in the same directory called "chopin.c" which contains
|
||||||
|
* the C-language statement to intiialize an array called "score" with the bytestream.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The general form for command line execution is this:
|
||||||
|
*
|
||||||
|
* miditones [-p] [-lg] [-lp] [-s1] [-tn] [-b] [-cn] [-kn] <basefilename>
|
||||||
|
*
|
||||||
|
* The <basefilename> is the base name, without an extension, for the input and
|
||||||
|
* output files. It can contain directory path information, or not.
|
||||||
|
*
|
||||||
|
* The input file is the base name with the extension ".mid". The output filename(s)
|
||||||
|
* are the base name with ".c", ".bin", and/or ".log" extensions.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* The following command-line options can be specified:
|
||||||
|
*
|
||||||
|
* -p Only parse the MIDI file; don't generate an output file.
|
||||||
|
* Tracks are processed sequentially instead of being merged into chronological order.
|
||||||
|
* This is mostly useful when generating a log to debug MIDI file parsing problems.
|
||||||
|
*
|
||||||
|
* -lp Log input file parsing information to the <basefilename>.log file
|
||||||
|
*
|
||||||
|
* -lg Log output bytestream generation information to the <basefilename>.log file
|
||||||
|
*
|
||||||
|
* -sn Use bytestream generation strategy "n".
|
||||||
|
* Two strategies are currently implemented:
|
||||||
|
* 1: favor track 1 notes instead of all tracks equally
|
||||||
|
* 2: try to keep each track to its own tone generator
|
||||||
|
*
|
||||||
|
* -tn Generate the bytestream so that at most n tone generators are used.
|
||||||
|
* The default is 6 tone generators, and the maximum is 16.
|
||||||
|
* The program will report how many notes had to be discarded because there
|
||||||
|
* weren't enough tone generators. Note that for the Arduino Playtunes
|
||||||
|
* library, it's ok to have the bytestream use more tone genreators than
|
||||||
|
* exist on your processor because any extra notes will be ignored, although
|
||||||
|
* it does make the file bigger than necessary . Of course, too many ignored
|
||||||
|
* notes will make the music sound really strange!
|
||||||
|
*
|
||||||
|
* -b Generate a binary file with the name <basefilename>.bin, instead of a
|
||||||
|
* C-language source file with the name <basefilename>.c.
|
||||||
|
*
|
||||||
|
* -cn Only process the channel numbers whose bits are on in the number "n".
|
||||||
|
* For example, -c3 means "only process channels 0 and 1"
|
||||||
|
*
|
||||||
|
* -kn Change the musical key of the output by n chromatic notes.
|
||||||
|
* -k-12 goes one octave down, -k12 goes one octave up, etc.
|
||||||
|
*
|
||||||
|
* -v Add velocity information to output
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ***** The score bytestream *****
|
||||||
|
*
|
||||||
|
* The generated bytestream is a series of commands that turn notes on and off, and
|
||||||
|
* start delays until the next note change. Here are the details, with numbers
|
||||||
|
* shown in hexadecimal.
|
||||||
|
*
|
||||||
|
* If the high-order bit of the byte is 1, then it is one of the following commands:
|
||||||
|
*
|
||||||
|
* 9t nn [vv] Start playing note nn on tone generator t. Generators are numbered
|
||||||
|
* starting with 0. The notes numbers are the MIDI numbers for the chromatic
|
||||||
|
* scale, with decimal 60 being Middle C, and decimal 69 being Middle A (440 Hz).
|
||||||
|
* if the -v option is enabled, a second byte is added to indicate velocity
|
||||||
|
*
|
||||||
|
* 8t Stop playing the note on tone generator t.
|
||||||
|
*
|
||||||
|
* F0 End of score: stop playing.
|
||||||
|
*
|
||||||
|
* E0 End of score: start playing again from the beginning.
|
||||||
|
* (Shown for completeness; MIDITONES won't generate this.)
|
||||||
|
*
|
||||||
|
* If the high-order bit of the byte is 0, it is a command to delay for a while until
|
||||||
|
* the next note change.. The other 7 bits and the 8 bits of the following byte are
|
||||||
|
* interpreted as a 15-bit big-endian integer that is the number of milliseconds to
|
||||||
|
* wait before processing the next command. For example,
|
||||||
|
*
|
||||||
|
* 07 D0
|
||||||
|
*
|
||||||
|
* would cause a delay of 0x07d0 = 2000 decimal millisconds, or 2 seconds. Any tones
|
||||||
|
* that were playing before the delay command will continue to play.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Len Shustek, 4 Feb 2011
|
||||||
|
*
|
||||||
|
*----------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
|
||||||
|
/*********** MIDI file header formats *****************/
|
||||||
|
|
||||||
|
struct midi_header {
|
||||||
|
int8_t MThd[4];
|
||||||
|
uint32_t header_size;
|
||||||
|
uint16_t format_type;
|
||||||
|
uint16_t number_of_tracks;
|
||||||
|
uint16_t time_division;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct track_header {
|
||||||
|
int8_t MTrk[4];
|
||||||
|
uint32_t track_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*********** Global variables ******************/
|
||||||
|
|
||||||
|
#define MAX_TONEGENS 16 /* max tone generators: tones we can play simultaneously */
|
||||||
|
#define DEFAULT_TONEGENS 6 /* default number of tone generators */
|
||||||
|
#define MAX_TRACKS 24 /* max number of MIDI tracks we will process */
|
||||||
|
|
||||||
|
bool loggen, logparse, parseonly, strategy1, strategy2, binaryoutput, velocityoutput;
|
||||||
|
FILE *infile, *outfile, *logfile;
|
||||||
|
uint8_t *buffer, *hdrptr;
|
||||||
|
unsigned long buflen;
|
||||||
|
int num_tracks;
|
||||||
|
int tracks_done = 0;
|
||||||
|
int outfile_itemcount = 0;
|
||||||
|
int num_tonegens = DEFAULT_TONEGENS;
|
||||||
|
int num_tonegens_used = 0;
|
||||||
|
unsigned channel_mask = 0xffff; // bit mask of channels to process
|
||||||
|
int keyshift = 0; // optional chromatic note shift for output file
|
||||||
|
long int outfile_bytecount = 0;
|
||||||
|
unsigned int ticks_per_beat = 240;
|
||||||
|
unsigned long timenow = 0;
|
||||||
|
unsigned long tempo; /* current tempo in usec/qnote */
|
||||||
|
|
||||||
|
struct tonegen_status { /* current status of a tone generator */
|
||||||
|
bool playing; /* is it playing? */
|
||||||
|
int track; /* if so, which track is the note from? */
|
||||||
|
int note; /* what note is playing? */
|
||||||
|
}
|
||||||
|
tonegen [MAX_TONEGENS] = {
|
||||||
|
{0}};
|
||||||
|
|
||||||
|
struct track_status { /* current processing point of a MIDI track */
|
||||||
|
uint8_t *trkptr; /* ptr to the next note change */
|
||||||
|
uint8_t *trkend; /* ptr past the end of the track */
|
||||||
|
unsigned long time; /* what time we're at in the score */
|
||||||
|
unsigned long tempo; /* the tempo last set, in usec/qnote */
|
||||||
|
unsigned int preferred_tonegen; /* for strategy2: try to use this generator */
|
||||||
|
unsigned char cmd; /* CMD_xxxx next to do */
|
||||||
|
unsigned char note; /* for which note */
|
||||||
|
unsigned char velocity;
|
||||||
|
unsigned char last_event; /* the last event, for MIDI's "running status" */
|
||||||
|
bool tonegens[MAX_TONEGENS];/* which tone generators our notes are playing on */
|
||||||
|
}
|
||||||
|
track[MAX_TRACKS] = {
|
||||||
|
{0}};
|
||||||
|
|
||||||
|
|
||||||
|
/* output bytestream commands, which are also stored in track_status.cmd */
|
||||||
|
|
||||||
|
#define CMD_PLAYNOTE 0x90 /* play a note: low nibble is generator #, note is next byte */
|
||||||
|
#define CMD_STOPNOTE 0x80 /* stop a note: low nibble is generator # */
|
||||||
|
#define CMD_RESTART 0xe0 /* restart the score from the beginning */
|
||||||
|
#define CMD_STOP 0xf0 /* stop playing */
|
||||||
|
/* if CMD < 0x80, then the other 7 bits and the next byte are a 15-bit number of msec to delay */
|
||||||
|
|
||||||
|
/* these other commands stored in the track_status.com */
|
||||||
|
#define CMD_TEMPO 0xFE /* tempo in usec per quarter note ("beat") */
|
||||||
|
#define CMD_TRACKDONE 0xFF /* no more data left in this track */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************** command-line processing *******************/
|
||||||
|
|
||||||
|
void SayUsage(char *programName){
|
||||||
|
static char *usage[] = {
|
||||||
|
"Convert MIDI files to an Arduino PLAYTUNE bytestream",
|
||||||
|
"miditones [-p] [-lg] [-lp] [-s1] [-tn] [-v] <basefilename>",
|
||||||
|
" -p parse only, don't generate bytestream",
|
||||||
|
" -lp log input parsing",
|
||||||
|
" -lg log output generation",
|
||||||
|
" -s1 strategy 1: favor track 1",
|
||||||
|
" -s2 strategy 2: try to assign tracks to specific tone generators",
|
||||||
|
" -tn use at most n tone generators (default is 6, max is 16)",
|
||||||
|
" -b binary file output instead of C source text",
|
||||||
|
" -cn mask for which tracks to process, e.g. -c3 for only 0 and 1",
|
||||||
|
" -kn key shift in chromatic notes, positive or negative",
|
||||||
|
" -v include velocity data in play note commands",
|
||||||
|
"input file: <basefilename>.mid",
|
||||||
|
"output file: <basefilename>.bin or .c",
|
||||||
|
"log file: <basefilename>.log",
|
||||||
|
"" };
|
||||||
|
int i=0;
|
||||||
|
while (usage[i][0] != '\0') fprintf(stderr, "%s\n", usage[i++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HandleOptions(int argc,char *argv[]) {
|
||||||
|
/* returns the index of the first argument that is not an option; i.e.
|
||||||
|
does not start with a dash or a slash*/
|
||||||
|
|
||||||
|
int i,firstnonoption=0;
|
||||||
|
|
||||||
|
/* --- The following skeleton comes from C:\lcc\lib\wizard\textmode.tpl. */
|
||||||
|
for (i=1; i< argc;i++) {
|
||||||
|
if (argv[i][0] == '/' || argv[i][0] == '-') {
|
||||||
|
switch (toupper(argv[i][1])) {
|
||||||
|
case 'H':
|
||||||
|
case '?':
|
||||||
|
SayUsage(argv[0]);
|
||||||
|
exit(1);
|
||||||
|
case 'L':
|
||||||
|
if (toupper(argv[i][2]) == 'G') loggen = true;
|
||||||
|
else if (toupper(argv[i][2]) == 'P') logparse = true;
|
||||||
|
else goto opterror;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
parseonly = true;
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
binaryoutput = true;
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
velocityoutput = true;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
if (argv[i][2] == '1') strategy1 = true;
|
||||||
|
else if (argv[i][2] == '2') strategy2 = true;
|
||||||
|
else goto opterror;
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
if (sscanf(&argv[i][2],"%d",&num_tonegens) != 1 || num_tonegens <1 || num_tonegens > MAX_TONEGENS) goto opterror;
|
||||||
|
printf("Using %d tone generators.\n", num_tonegens);
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
if (sscanf(&argv[i][2],"%d",&channel_mask) != 1 || channel_mask > 0xffff) goto opterror;
|
||||||
|
printf("Channel (track) mask is %04X.\n", channel_mask);
|
||||||
|
break;
|
||||||
|
case 'K':
|
||||||
|
if (sscanf(&argv[i][2],"%d",&keyshift) != 1 || keyshift < -100 || keyshift > 100) goto opterror;
|
||||||
|
printf("Using keyshift %d.\n", keyshift);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* add more option switches here */
|
||||||
|
opterror:
|
||||||
|
default:
|
||||||
|
fprintf(stderr,"unknown option: %s\n",argv[i]);
|
||||||
|
SayUsage(argv[0]);
|
||||||
|
exit(4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
firstnonoption = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return firstnonoption;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_command_line (int argc,char *argv[]) {
|
||||||
|
int i;
|
||||||
|
fprintf(outfile, "// command line: ");
|
||||||
|
for (i=0; i< argc; i++) fprintf(outfile,"%s ",argv[i]);
|
||||||
|
fprintf(outfile, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************** utility routines **********************/
|
||||||
|
|
||||||
|
|
||||||
|
/* safe string copy */
|
||||||
|
size_t miditones_strlcpy(char *dst, const char *src, size_t siz) {
|
||||||
|
char *d = dst;
|
||||||
|
const char *s = src;
|
||||||
|
size_t n = siz;
|
||||||
|
/* Copy as many bytes as will fit */
|
||||||
|
if (n != 0)
|
||||||
|
{
|
||||||
|
while (--n != 0)
|
||||||
|
{
|
||||||
|
if ((*d++ = *s++) == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Not enough room in dst, add NUL and traverse rest of src */
|
||||||
|
if (n == 0)
|
||||||
|
{
|
||||||
|
if (siz != 0)
|
||||||
|
*d = '\0'; /* NUL-terminate dst */
|
||||||
|
while (*s++)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
return (s - src - 1); /* count does not include NUL */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* safe string concatenation */
|
||||||
|
|
||||||
|
size_t miditones_strlcat(char *dst, const char *src, size_t siz) {
|
||||||
|
char *d = dst;
|
||||||
|
const char *s = src;
|
||||||
|
size_t n = siz;
|
||||||
|
size_t dlen;
|
||||||
|
/* Find the end of dst and adjust bytes left but don't go past end */
|
||||||
|
while (n-- != 0 && *d != '\0')
|
||||||
|
d++;
|
||||||
|
dlen = d - dst;
|
||||||
|
n = siz - dlen;
|
||||||
|
if (n == 0)
|
||||||
|
return (dlen + strlen(s));
|
||||||
|
while (*s != '\0')
|
||||||
|
{
|
||||||
|
if (n != 1)
|
||||||
|
{
|
||||||
|
*d++ = *s;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
*d = '\0';
|
||||||
|
return (dlen + (s - src)); /* count does not include NUL */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* match a constant character sequence */
|
||||||
|
|
||||||
|
int charcmp (const char *buf, const char *match) {
|
||||||
|
int len, i;
|
||||||
|
len = strlen (match);
|
||||||
|
for (i=0; i<len; ++i)
|
||||||
|
if (buf[i] != match[i]) return 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* announce a fatal MIDI file format error */
|
||||||
|
|
||||||
|
void midi_error (char *msg, unsigned char *bufptr) {
|
||||||
|
unsigned char *ptr;
|
||||||
|
fprintf(stderr, "---> MIDI file error at position %04X (%d): %s\n", (uint16_t)(bufptr-buffer), (uint16_t)(bufptr-buffer), msg);
|
||||||
|
/* print some bytes surrounding the error */
|
||||||
|
ptr = bufptr - 16;
|
||||||
|
if (ptr < buffer) ptr = buffer;
|
||||||
|
for (; ptr <= bufptr+16 && ptr < buffer+buflen; ++ptr) fprintf (stderr, ptr==bufptr ? " [%02X] ":"%02X ", *ptr);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
exit(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check that we have a specified number of bytes left in the buffer */
|
||||||
|
|
||||||
|
void chk_bufdata(unsigned char *ptr, unsigned long int len) {
|
||||||
|
if ((unsigned)(ptr + len - buffer) > buflen) midi_error("data missing", ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* fetch big-endian numbers */
|
||||||
|
|
||||||
|
uint16_t rev_short (uint16_t val) {
|
||||||
|
return ((val&0xff)<<8) | ((val>>8)&0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t rev_long (uint32_t val){
|
||||||
|
return (((rev_short((uint16_t)val) & 0xffff) << 16) |
|
||||||
|
(rev_short((uint16_t)(val >> 16)) & 0xffff));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* account for new items in the non-binary output file
|
||||||
|
and generate a newline every so often. */
|
||||||
|
|
||||||
|
void outfile_items (int n) {
|
||||||
|
outfile_bytecount += n;
|
||||||
|
outfile_itemcount += n;
|
||||||
|
if (!binaryoutput && outfile_itemcount > 20) {
|
||||||
|
fprintf (outfile, "\n");
|
||||||
|
outfile_itemcount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************** process the MIDI file header *****************/
|
||||||
|
|
||||||
|
void process_header (void) {
|
||||||
|
struct midi_header *hdr;
|
||||||
|
unsigned int time_division;
|
||||||
|
|
||||||
|
chk_bufdata(hdrptr, sizeof(struct midi_header));
|
||||||
|
hdr = (struct midi_header *) hdrptr;
|
||||||
|
if (!charcmp((char*)hdr->MThd,"MThd")) midi_error("Missing 'MThd'", hdrptr);
|
||||||
|
|
||||||
|
num_tracks = rev_short(hdr->number_of_tracks);
|
||||||
|
|
||||||
|
time_division = rev_short(hdr->time_division);
|
||||||
|
if (time_division < 0x8000) ticks_per_beat = time_division;
|
||||||
|
else ticks_per_beat = ((time_division >> 8) & 0x7f) /* SMTE frames/sec */ * (time_division & 0xff); /* ticks/SMTE frame */
|
||||||
|
|
||||||
|
if (logparse) {
|
||||||
|
fprintf (logfile, "Header size %" PRId32 "\n", rev_long(hdr->header_size));
|
||||||
|
fprintf (logfile, "Format type %d\n", rev_short(hdr->format_type));
|
||||||
|
fprintf (logfile, "Number of tracks %d\n", num_tracks);
|
||||||
|
fprintf (logfile, "Time division %04X\n", time_division);
|
||||||
|
fprintf (logfile, "Ticks/beat = %d\n", ticks_per_beat);
|
||||||
|
|
||||||
|
}
|
||||||
|
hdrptr += rev_long(hdr->header_size) + 8; /* point past header to track header, presumably. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**************** Process a MIDI track header *******************/
|
||||||
|
|
||||||
|
void start_track (int tracknum) {
|
||||||
|
struct track_header *hdr;
|
||||||
|
unsigned long tracklen;
|
||||||
|
|
||||||
|
chk_bufdata(hdrptr, sizeof(struct track_header));
|
||||||
|
hdr = (struct track_header *) hdrptr;
|
||||||
|
if (!charcmp((char *)(hdr->MTrk),"MTrk")) midi_error("Missing 'MTrk'", hdrptr);
|
||||||
|
|
||||||
|
tracklen = rev_long(hdr->track_size);
|
||||||
|
if (logparse) fprintf (logfile, "\nTrack %d length %ld\n", tracknum, tracklen);
|
||||||
|
hdrptr += sizeof (struct track_header); /* point past header */
|
||||||
|
chk_bufdata(hdrptr, tracklen);
|
||||||
|
track[tracknum].trkptr = hdrptr;
|
||||||
|
hdrptr += tracklen; /* point to the start of the next track */
|
||||||
|
track[tracknum].trkend = hdrptr; /* the point past the end of the track */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get a MIDI-style variable-length integer */
|
||||||
|
|
||||||
|
unsigned long get_varlen (uint8_t **ptr) {
|
||||||
|
/* Get a 1-4 byte variable-length value and adjust the pointer past it.
|
||||||
|
These are a succession of 7-bit values with a MSB bit of zero marking the end */
|
||||||
|
|
||||||
|
unsigned long val;
|
||||||
|
int i, byte;
|
||||||
|
|
||||||
|
val = 0;
|
||||||
|
for (i=0; i<4; ++i){
|
||||||
|
byte = *(*ptr)++;
|
||||||
|
val = (val<<7) | (byte&0x7f);
|
||||||
|
if (!(byte&0x80)) return val;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*************** Process the MIDI track data ***************************/
|
||||||
|
|
||||||
|
/* Skip in the track for the next "note on", "note off" or "set tempo" command,
|
||||||
|
then record that information in the track status block and return. */
|
||||||
|
|
||||||
|
void find_note (int tracknum) {
|
||||||
|
unsigned long int delta_time;
|
||||||
|
int event, chan;
|
||||||
|
int i;
|
||||||
|
int note, velocity;
|
||||||
|
int meta_cmd, meta_length;
|
||||||
|
unsigned long int sysex_length;
|
||||||
|
struct track_status *t;
|
||||||
|
|
||||||
|
/* process events */
|
||||||
|
|
||||||
|
t = &track[tracknum]; /* our track status structure */
|
||||||
|
while (t->trkptr < t->trkend) {
|
||||||
|
|
||||||
|
delta_time = get_varlen(&t->trkptr);
|
||||||
|
if (logparse) {
|
||||||
|
fprintf (logfile, "trk %d ", tracknum);
|
||||||
|
if (delta_time) {
|
||||||
|
fprintf (logfile, "delta time %4ld, ", delta_time);
|
||||||
|
} else {
|
||||||
|
fprintf (logfile, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t->time += delta_time;
|
||||||
|
|
||||||
|
if (*t->trkptr < 0x80) /* "running status" */ event = t->last_event;/* means same event as before */
|
||||||
|
else { /* new "status" (event type) */
|
||||||
|
event = *t->trkptr++;
|
||||||
|
t->last_event = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event == 0xff) { /* meta-event */
|
||||||
|
meta_cmd = *t->trkptr++;
|
||||||
|
meta_length = *t->trkptr++;
|
||||||
|
switch (meta_cmd) {
|
||||||
|
case 0x2f:
|
||||||
|
if (logparse) fprintf(logfile, "end of track\n");
|
||||||
|
break;
|
||||||
|
case 0x00:
|
||||||
|
if (logparse) fprintf(logfile, "sequence number %d\n", rev_short(*(unsigned short *)t->trkptr));
|
||||||
|
break;
|
||||||
|
case 0x20:
|
||||||
|
if (logparse) fprintf(logfile, "channel prefix %d\n", *t->trkptr);
|
||||||
|
break;
|
||||||
|
case 0x51: /* tempo: 3 byte big-endian integer! */
|
||||||
|
t->cmd = CMD_TEMPO;
|
||||||
|
t->tempo = rev_long(*(unsigned long *)(t->trkptr-1)) & 0xffffffL;
|
||||||
|
if (logparse) fprintf(logfile, "set tempo %ld usec/qnote\n", t->tempo);
|
||||||
|
t->trkptr += meta_length;
|
||||||
|
return;
|
||||||
|
case 0x54:
|
||||||
|
if (logparse) fprintf(logfile, "SMPTE offset %08" PRIx32 "\n", rev_long(*(unsigned long *)t->trkptr));
|
||||||
|
break;
|
||||||
|
case 0x58:
|
||||||
|
if (logparse) fprintf(logfile, "time signature %08" PRIx32 "\n", rev_long(*(unsigned long *)t->trkptr));
|
||||||
|
break;
|
||||||
|
case 0x59:
|
||||||
|
if (logparse) fprintf(logfile, "key signature %04X\n", rev_short(*(unsigned short *)t->trkptr));
|
||||||
|
break;
|
||||||
|
default: /* assume it is a string */
|
||||||
|
if (logparse) {
|
||||||
|
fprintf(logfile, "meta cmd %02X, length %d, \"", meta_cmd, meta_length);
|
||||||
|
for (i=0; i<meta_length; ++i) {
|
||||||
|
int ch = t->trkptr[i];
|
||||||
|
fprintf(logfile, "%c", isprint(ch) ? ch : '?');
|
||||||
|
}
|
||||||
|
fprintf(logfile, "\"\n");
|
||||||
|
}
|
||||||
|
if (tracknum==0 && meta_cmd==0x03 && !parseonly && !binaryoutput) {
|
||||||
|
/* Incredibly, MIDI has no standard for recording the name of the piece!
|
||||||
|
Track 0's "trackname" (meta 0x03) is sometimes used for that, so
|
||||||
|
we output it to the C file as documentation. */
|
||||||
|
fprintf(outfile, "// ");
|
||||||
|
for (i=0; i<meta_length; ++i) {
|
||||||
|
int ch = t->trkptr[i];
|
||||||
|
fprintf(outfile, "%c", isprint(ch) ? ch : '?');
|
||||||
|
}
|
||||||
|
fprintf(outfile, "\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
t->trkptr += meta_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (event <0x80) midi_error("Unknown MIDI event type", t->trkptr);
|
||||||
|
|
||||||
|
else {
|
||||||
|
chan = event & 0xf;
|
||||||
|
switch (event>>4) {
|
||||||
|
case 0x8:
|
||||||
|
t->note = *t->trkptr++;
|
||||||
|
velocity = *t->trkptr++;
|
||||||
|
note_off:
|
||||||
|
if (logparse) fprintf (logfile, "note %02X off, chan %d, velocity %d\n", t->note, chan, velocity);
|
||||||
|
if ((1<<chan) & channel_mask) { // if we're processing this channel
|
||||||
|
t->cmd = CMD_STOPNOTE;
|
||||||
|
return; /* stop processing and return */
|
||||||
|
}
|
||||||
|
break; // else keep looking
|
||||||
|
case 0x9:
|
||||||
|
t->note = *t->trkptr++;
|
||||||
|
velocity = *t->trkptr++;
|
||||||
|
if (velocity == 0) /* some scores use note-on with zero velocity for off! */ goto note_off;
|
||||||
|
t->velocity = velocity;
|
||||||
|
if (logparse) fprintf (logfile, "note %02X on, chan %d, velocity %d\n", t->note, chan, velocity);
|
||||||
|
if ((1<<chan) & channel_mask) { // if we're processing this channel
|
||||||
|
t->cmd = CMD_PLAYNOTE;
|
||||||
|
return; /* stop processing and return */
|
||||||
|
}
|
||||||
|
break; // else keep looking
|
||||||
|
case 0xa:
|
||||||
|
note = *t->trkptr++;
|
||||||
|
velocity = *t->trkptr++;
|
||||||
|
if (logparse) fprintf (logfile, "after-touch %02X, %02X\n", note, velocity);
|
||||||
|
break;
|
||||||
|
case 0xb:
|
||||||
|
note = *t->trkptr++;
|
||||||
|
velocity = *t->trkptr++;
|
||||||
|
if (logparse) fprintf (logfile, "control change %02X, %02X\n", note, velocity);
|
||||||
|
break;
|
||||||
|
case 0xc:
|
||||||
|
note = *t->trkptr++;
|
||||||
|
if (logparse) fprintf(logfile, "program patch %02X\n", note);
|
||||||
|
break;
|
||||||
|
case 0xd:
|
||||||
|
chan = *t->trkptr++;
|
||||||
|
if (logparse) fprintf(logfile, "channel after-touch %02X\n", chan);
|
||||||
|
break;
|
||||||
|
case 0xe:
|
||||||
|
note = *t->trkptr++;
|
||||||
|
velocity = *t->trkptr++;
|
||||||
|
if (logparse) fprintf(logfile, "pitch wheel change %02X, %02X\n", note, velocity);
|
||||||
|
break;
|
||||||
|
case 0xf:
|
||||||
|
sysex_length = get_varlen(&t->trkptr);
|
||||||
|
if (logparse) fprintf(logfile, "SysEx event %02X, %ld bytes\n", event, sysex_length);
|
||||||
|
t->trkptr += sysex_length;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
midi_error("Unknown MIDI command", t->trkptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t->cmd = CMD_TRACKDONE; /* no more notes to process */
|
||||||
|
++tracks_done;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/********************* main ****************************/
|
||||||
|
|
||||||
|
int main(int argc,char *argv[]) {
|
||||||
|
int argno;
|
||||||
|
char *filebasename;
|
||||||
|
#define MAXPATH 120
|
||||||
|
char filename[MAXPATH];
|
||||||
|
|
||||||
|
int tracknum;
|
||||||
|
int earliest_tracknum;
|
||||||
|
unsigned long earliest_time;
|
||||||
|
int notes_skipped = 0;
|
||||||
|
|
||||||
|
printf("MIDITONES V%s, (C) 2011,2015,2016 Len Shustek\n", VERSION);
|
||||||
|
printf("See the source code for license information.\n\n");
|
||||||
|
if (argc == 1) { /* no arguments */
|
||||||
|
SayUsage(argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* process options */
|
||||||
|
|
||||||
|
argno = HandleOptions(argc,argv);
|
||||||
|
filebasename = argv[argno];
|
||||||
|
|
||||||
|
/* Open the log file */
|
||||||
|
|
||||||
|
if (logparse || loggen) {
|
||||||
|
miditones_strlcpy(filename, filebasename, MAXPATH);
|
||||||
|
miditones_strlcat(filename, ".log", MAXPATH);
|
||||||
|
logfile = fopen(filename, "w");
|
||||||
|
if (!logfile) {
|
||||||
|
fprintf(stderr, "Unable to open log file %s", filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open the input file */
|
||||||
|
|
||||||
|
miditones_strlcpy(filename, filebasename, MAXPATH);
|
||||||
|
miditones_strlcat(filename, ".mid", MAXPATH);
|
||||||
|
infile = fopen(filename, "rb");
|
||||||
|
if (!infile) {
|
||||||
|
fprintf(stderr, "Unable to open input file %s", filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the whole input file into memory */
|
||||||
|
|
||||||
|
fseek(infile, 0, SEEK_END); /* find file size */
|
||||||
|
buflen = ftell(infile);
|
||||||
|
fseek(infile, 0, SEEK_SET);
|
||||||
|
|
||||||
|
buffer = (unsigned char *) malloc (buflen+1);
|
||||||
|
if (!buffer) {
|
||||||
|
fprintf(stderr, "Unable to allocate %ld bytes for the file", buflen);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(buffer, buflen, 1, infile);
|
||||||
|
fclose(infile);
|
||||||
|
if (logparse) fprintf(logfile, "Processing %s, %ld bytes\n", filename, buflen);
|
||||||
|
|
||||||
|
/* Create the output file */
|
||||||
|
|
||||||
|
if (!parseonly) {
|
||||||
|
miditones_strlcpy(filename, filebasename, MAXPATH);
|
||||||
|
if (binaryoutput) {
|
||||||
|
miditones_strlcat(filename, ".bin", MAXPATH);
|
||||||
|
outfile = fopen(filename, "wb");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
miditones_strlcat(filename, ".c", MAXPATH);
|
||||||
|
outfile = fopen(filename, "w");
|
||||||
|
}
|
||||||
|
if (!outfile) {
|
||||||
|
fprintf(stderr, "Unable to open output file %s", filename);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!binaryoutput) { /* create header of C file that initializes score data */
|
||||||
|
time_t rawtime;
|
||||||
|
time (&rawtime);
|
||||||
|
fprintf(outfile, "// Playtune bytestream for file \"%s.mid\" ", filebasename);
|
||||||
|
fprintf(outfile, "created by MIDITONES V%s on %s", VERSION, asctime(localtime(&rawtime)));
|
||||||
|
print_command_line(argc,argv);
|
||||||
|
if (channel_mask != 0xffff)
|
||||||
|
fprintf(outfile, "// Only the masked channels were processed: %04X\n", channel_mask);
|
||||||
|
if (keyshift != 0)
|
||||||
|
fprintf(outfile, "// Keyshift was %d chromatic notes\n", keyshift);
|
||||||
|
fprintf(outfile, "#ifdef __AVR__\n");
|
||||||
|
fprintf(outfile, "#include <avr/pgmspace.h>\n");
|
||||||
|
fprintf(outfile, "#else\n");
|
||||||
|
fprintf(outfile, "#define PROGMEM\n");
|
||||||
|
fprintf(outfile, "#endif\n");
|
||||||
|
fprintf(outfile, "const unsigned char PROGMEM score [] = {\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* process the MIDI file header */
|
||||||
|
|
||||||
|
hdrptr = buffer; /* pointer to file and track headers */
|
||||||
|
process_header ();
|
||||||
|
printf (" Processing %d tracks.\n", num_tracks);
|
||||||
|
if (num_tracks > MAX_TRACKS) midi_error ("Too many tracks", buffer);
|
||||||
|
|
||||||
|
/* initialize processing of all the tracks */
|
||||||
|
|
||||||
|
for (tracknum=0; tracknum < num_tracks; ++tracknum) {
|
||||||
|
start_track (tracknum); /* process the track header */
|
||||||
|
find_note (tracknum); /* position to the first note on/off */
|
||||||
|
/* if we are in "parse only" mode, do the whole track,
|
||||||
|
so we do them one at a time instead of time-synchronized. */
|
||||||
|
if (parseonly) while (track[tracknum].cmd != CMD_TRACKDONE) find_note(tracknum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Continue processing all tracks, in an order based on the simulated time.
|
||||||
|
This is not unlike multiway merging used for tape sorting algoritms in the 50's! */
|
||||||
|
|
||||||
|
tracknum = 0;
|
||||||
|
if (!parseonly) do { /* while there are still track notes to process */
|
||||||
|
struct track_status *trk;
|
||||||
|
struct tonegen_status *tg;
|
||||||
|
int tgnum;
|
||||||
|
int count_tracks;
|
||||||
|
unsigned long delta_time, delta_msec;
|
||||||
|
|
||||||
|
/* Find the track with the earliest event time,
|
||||||
|
and output a delay command if time has advanced.
|
||||||
|
|
||||||
|
A potential improvement: If there are multiple tracks with the same time,
|
||||||
|
first do the ones with STOPNOTE as the next command, if any. That would
|
||||||
|
help avoid running out of tone generators. In practice, though, most MIDI
|
||||||
|
files do all the STOPNOTEs first anyway, so it won't have much effect.
|
||||||
|
*/
|
||||||
|
|
||||||
|
earliest_time = 0x7fffffff;
|
||||||
|
|
||||||
|
/* Usually we start with the track after the one we did last time (tracknum),
|
||||||
|
so that if we run out of tone generators, we have been fair to all the tracks.
|
||||||
|
The alternate "strategy1" says we always start with track 0, which means
|
||||||
|
that we favor early tracks over later ones when there aren't enough tone generators.
|
||||||
|
*/
|
||||||
|
|
||||||
|
count_tracks = num_tracks;
|
||||||
|
if (strategy1) tracknum = num_tracks; /* beyond the end, so we start with track 0 */
|
||||||
|
do {
|
||||||
|
if (++tracknum >= num_tracks) tracknum=0;
|
||||||
|
trk = &track[tracknum];
|
||||||
|
if (trk->cmd != CMD_TRACKDONE && trk->time < earliest_time) {
|
||||||
|
earliest_time = trk->time;
|
||||||
|
earliest_tracknum = tracknum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (--count_tracks);
|
||||||
|
|
||||||
|
tracknum = earliest_tracknum; /* the track we picked */
|
||||||
|
trk = &track[tracknum];
|
||||||
|
|
||||||
|
if (loggen) fprintf (logfile, "Earliest time is trk %d, time %ld\n", tracknum, earliest_time);
|
||||||
|
if (earliest_time < timenow) midi_error ("INTERNAL: time went backwards", trk->trkptr);
|
||||||
|
|
||||||
|
/* If time has advanced, output a "delay" command */
|
||||||
|
|
||||||
|
delta_time = earliest_time - timenow;
|
||||||
|
if (delta_time) {
|
||||||
|
/* Convert ticks to milliseconds based on the current tempo */
|
||||||
|
delta_msec = ((unsigned long) delta_time * tempo) / ticks_per_beat / 1000;
|
||||||
|
if (loggen) fprintf (logfile, "->Delay %ld msec (%ld ticks)\n", delta_msec, delta_time);
|
||||||
|
if (delta_msec > 0x7fff) midi_error ("INTERNAL: time delta too big", trk->trkptr);
|
||||||
|
/* output a 15-bit delay in big-endian format */
|
||||||
|
if (binaryoutput) {
|
||||||
|
putc ((unsigned char) (delta_msec >> 8), outfile);
|
||||||
|
putc ((unsigned char) (delta_msec & 0xff), outfile);
|
||||||
|
outfile_bytecount += 2;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf (outfile, "%ld,%ld, ", delta_msec >> 8, delta_msec & 0xff);
|
||||||
|
outfile_items(2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
timenow = earliest_time;
|
||||||
|
|
||||||
|
/* If this track event is "set tempo", just change the global tempo.
|
||||||
|
That affects how we generate "delay" commands. */
|
||||||
|
|
||||||
|
if (trk->cmd == CMD_TEMPO) {
|
||||||
|
tempo = trk->tempo;
|
||||||
|
if (loggen) fprintf (logfile, "Tempo changed to %ld usec/qnote\n", tempo);
|
||||||
|
find_note (tracknum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this track event is "stop note", process it and all subsequent "stop notes" for this track
|
||||||
|
that are happening at the same time. Doing so frees up as many tone generators as possible. */
|
||||||
|
|
||||||
|
else if (trk->cmd == CMD_STOPNOTE) do {
|
||||||
|
|
||||||
|
// stop a note
|
||||||
|
for (tgnum=0; tgnum < num_tonegens; ++tgnum) { /* find which generator is playing it */
|
||||||
|
tg = &tonegen[tgnum];
|
||||||
|
if (tg->playing && tg->track == tracknum && tg->note == trk->note) {
|
||||||
|
if (loggen) fprintf (logfile, "->Stop note %02X, generator %d, track %d\n", tg->note, tgnum, tracknum);
|
||||||
|
if (binaryoutput) {
|
||||||
|
putc (CMD_STOPNOTE | tgnum, outfile);
|
||||||
|
outfile_bytecount += 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf (outfile, "0x%02X, ", CMD_STOPNOTE | tgnum);
|
||||||
|
outfile_items (1);
|
||||||
|
}
|
||||||
|
tg->playing = false;
|
||||||
|
trk->tonegens[tgnum] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
find_note (tracknum); // use up the note
|
||||||
|
}
|
||||||
|
while (trk->cmd == CMD_STOPNOTE && trk->time == timenow);
|
||||||
|
|
||||||
|
/* If this track event is "start note", process only it.
|
||||||
|
Don't do more than one, so we allow other tracks their chance at grabbing tone generators. */
|
||||||
|
|
||||||
|
else if (trk->cmd == CMD_PLAYNOTE) {
|
||||||
|
bool foundgen = false;
|
||||||
|
|
||||||
|
if (strategy2) { /* try to use the same tone generator this track used last time */
|
||||||
|
tg = &tonegen [trk->preferred_tonegen];
|
||||||
|
if (!tg->playing) {
|
||||||
|
tgnum = trk->preferred_tonegen;
|
||||||
|
foundgen = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundgen) for (tgnum=0; tgnum < num_tonegens; ++tgnum) { /* search for a free tone generator */
|
||||||
|
tg = &tonegen[tgnum];
|
||||||
|
if (!tg->playing) {
|
||||||
|
foundgen = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (foundgen) {
|
||||||
|
int shifted_note;
|
||||||
|
if (tgnum+1 > num_tonegens_used) num_tonegens_used = tgnum+1;
|
||||||
|
tg->playing = true;
|
||||||
|
tg->track = tracknum;
|
||||||
|
tg->note = trk->note;
|
||||||
|
trk->tonegens[tgnum] = true;
|
||||||
|
trk->preferred_tonegen = tgnum;
|
||||||
|
if (loggen) fprintf (logfile, "->Start note %02X, generator %d, track %d\n", trk->note, tgnum, tracknum);
|
||||||
|
shifted_note = trk->note + keyshift;
|
||||||
|
if (shifted_note < 0) shifted_note = 0;
|
||||||
|
if (shifted_note > 127) shifted_note = 127;
|
||||||
|
if (binaryoutput) {
|
||||||
|
putc (CMD_PLAYNOTE | tgnum, outfile);
|
||||||
|
putc (shifted_note, outfile);
|
||||||
|
outfile_bytecount += 2;
|
||||||
|
if (velocityoutput) {
|
||||||
|
putc (trk->velocity, outfile);
|
||||||
|
outfile_bytecount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (velocityoutput == 0) {
|
||||||
|
fprintf (outfile, "0x%02X,%d, ", CMD_PLAYNOTE | tgnum, shifted_note);
|
||||||
|
outfile_items(2);
|
||||||
|
} else {
|
||||||
|
fprintf (outfile, "0x%02X,%d,%d, ", CMD_PLAYNOTE | tgnum, shifted_note, trk->velocity);
|
||||||
|
outfile_items(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (loggen) fprintf (logfile, "----> No free generator, skipping note %02X, track %d\n", trk->note, tracknum);
|
||||||
|
++notes_skipped;
|
||||||
|
}
|
||||||
|
find_note (tracknum); // use up the note
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* !parseonly do */
|
||||||
|
while (tracks_done < num_tracks);
|
||||||
|
|
||||||
|
if (!parseonly) {
|
||||||
|
// generate the end-of-score command and some commentary
|
||||||
|
if(binaryoutput) putc(CMD_STOP, outfile);
|
||||||
|
else {
|
||||||
|
fprintf(outfile, "0x%02x};\n// This score contains %ld bytes, and %d tone generator%s used.\n", CMD_STOP, outfile_bytecount, num_tonegens_used, num_tonegens_used == 1 ? " is" : "s are");
|
||||||
|
if (notes_skipped) fprintf(outfile, "// %d notes had to be skipped.\n", notes_skipped);
|
||||||
|
}
|
||||||
|
printf (" %s %d tone generators were used.\n", num_tonegens_used < num_tonegens ? "Only":"All", num_tonegens_used);
|
||||||
|
if (notes_skipped) printf(" %d notes were skipped because there weren't enough tone generators.\n", notes_skipped);
|
||||||
|
printf (" %ld bytes of score data were generated.\n", outfile_bytecount);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
printf (" Done.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
356
sampler/lib/Audio_SdFat/extras/wav2sketch/wav2sketch.c
Normal file
356
sampler/lib/Audio_SdFat/extras/wav2sketch/wav2sketch.c
Normal file
|
|
@ -0,0 +1,356 @@
|
||||||
|
// Convert a set of WAV audio files to C data arrays for the Teensy3 Audio Library
|
||||||
|
// Copyright 2014, Paul Stoffregen (paul@pjrc.com)
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
// compile with: gcc -O2 -Wall -o wav2sketch wav2sketch.c
|
||||||
|
// i686-w64-mingw32-gcc -s -O2 -Wall wav2sketch.c -o wav2sketch.exe
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
uint8_t ulaw_encode(int16_t audio);
|
||||||
|
void print_byte(FILE *out, uint8_t b);
|
||||||
|
void filename2samplename(void);
|
||||||
|
uint32_t padding(uint32_t length, uint32_t block);
|
||||||
|
uint8_t read_uint8(FILE *in);
|
||||||
|
int16_t read_int16(FILE *in);
|
||||||
|
uint32_t read_uint32(FILE *in);
|
||||||
|
void die(const char *format, ...) __attribute__ ((format (printf, 1, 2)));
|
||||||
|
|
||||||
|
// WAV file format:
|
||||||
|
// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
|
||||||
|
|
||||||
|
const char *filename="";
|
||||||
|
char samplename[64];
|
||||||
|
unsigned int bcount, wcount;
|
||||||
|
unsigned int total_length=0;
|
||||||
|
int pcm_mode=0;
|
||||||
|
|
||||||
|
void wav2c(FILE *in, FILE *out, FILE *outh)
|
||||||
|
{
|
||||||
|
uint32_t header[4];
|
||||||
|
int16_t format, channels, bits;
|
||||||
|
uint32_t rate;
|
||||||
|
uint32_t i, length, padlength=0, arraylen;
|
||||||
|
uint32_t chunkSize;
|
||||||
|
int32_t audio=0;
|
||||||
|
|
||||||
|
// read the WAV file's header
|
||||||
|
for (i=0; i < 4; i++) {
|
||||||
|
header[i] = read_uint32(in);
|
||||||
|
}
|
||||||
|
while (header[3] != 0x20746D66) {
|
||||||
|
// skip past unknown sections until "fmt "
|
||||||
|
chunkSize = read_uint32(in);
|
||||||
|
for (i=0; i < chunkSize; i++) {
|
||||||
|
read_uint8(in);
|
||||||
|
}
|
||||||
|
header[3] = read_uint32(in);
|
||||||
|
}
|
||||||
|
chunkSize = read_uint32(in);
|
||||||
|
|
||||||
|
// read the audio format parameters
|
||||||
|
format = read_int16(in);
|
||||||
|
channels = read_int16(in);
|
||||||
|
rate = read_uint32(in);
|
||||||
|
read_uint32(in); // ignore byterate
|
||||||
|
read_int16(in); // ignore blockalign
|
||||||
|
bits = read_int16(in);
|
||||||
|
//printf("format: %d, channels: %d, rate: %d, bits %d\n", format, channels, rate, bits);
|
||||||
|
if (format != 1)
|
||||||
|
die("file %s is compressed, only uncompressed supported", filename);
|
||||||
|
if (rate != 44100 && rate != 22050 && rate != 11025 /*&& rate != 8000*/ )
|
||||||
|
die("sample rate %d in %s is unsupported\n"
|
||||||
|
"Only 44100, 22050, 11025 work", rate, filename);
|
||||||
|
if (channels != 1 && channels != 2)
|
||||||
|
die("file %s has %d channels, but only 1 & 2 are supported", filename, channels);
|
||||||
|
if (bits != 16)
|
||||||
|
die("file %s has %d bit format, but only 16 is supported", filename, bits);
|
||||||
|
|
||||||
|
// skip past any extra data on the WAVE header (hopefully it doesn't matter?)
|
||||||
|
for (chunkSize -= 16; chunkSize > 0; chunkSize--) {
|
||||||
|
read_uint8(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
// read the data header, skip non-audio data
|
||||||
|
while (1) {
|
||||||
|
header[0] = read_uint32(in);
|
||||||
|
length = read_uint32(in);
|
||||||
|
if (header[0] == 0x61746164) break; // beginning of actual audio data
|
||||||
|
// skip over non-audio data
|
||||||
|
for (i=0; i < length; i++) {
|
||||||
|
read_uint8(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the length must be a multiple of the data size
|
||||||
|
if (channels == 2) {
|
||||||
|
if (length % 4) die("file %s data length is not a multiple of 4", filename);
|
||||||
|
length = length / 4;
|
||||||
|
}
|
||||||
|
if (channels == 1) {
|
||||||
|
if (length % 1) die("file %s data length is not a multiple of 2", filename);
|
||||||
|
length = length / 2;
|
||||||
|
}
|
||||||
|
if (length > 0xFFFFFF) die("file %s data length is too long", filename);
|
||||||
|
bcount = 0;
|
||||||
|
|
||||||
|
// AudioPlayMemory requires padding to 2.9 ms boundary (128 samples @ 44100)
|
||||||
|
if (rate == 44100) {
|
||||||
|
padlength = padding(length, 128);
|
||||||
|
format = 1;
|
||||||
|
} else if (rate == 22050) {
|
||||||
|
padlength = padding(length, 64);
|
||||||
|
format = 2;
|
||||||
|
} else if (rate == 11025) {
|
||||||
|
padlength = padding(length, 32);
|
||||||
|
format = 3;
|
||||||
|
}
|
||||||
|
if (pcm_mode) {
|
||||||
|
arraylen = ((length + padlength) * 2 + 3) / 4 + 1;
|
||||||
|
format |= 0x80;
|
||||||
|
} else {
|
||||||
|
arraylen = (length + padlength + 3) / 4 + 1;
|
||||||
|
}
|
||||||
|
total_length += arraylen;
|
||||||
|
|
||||||
|
// output a minimal header, just the length, #bits and sample rate
|
||||||
|
fprintf(outh, "extern const unsigned int AudioSample%s[%d];\n", samplename, arraylen);
|
||||||
|
fprintf(out, "// Converted from %s, using %d Hz, %s encoding\n", filename, rate,
|
||||||
|
(pcm_mode ? "16 bit PCM" : "u-law"));
|
||||||
|
fprintf(out, "const unsigned int AudioSample%s[%d] = {\n", samplename, arraylen);
|
||||||
|
fprintf(out, "0x%08X,", length | (format << 24));
|
||||||
|
wcount = 1;
|
||||||
|
|
||||||
|
// finally, read the audio data
|
||||||
|
while (length > 0) {
|
||||||
|
if (channels == 1) {
|
||||||
|
audio = read_int16(in);
|
||||||
|
} else {
|
||||||
|
audio = read_int16(in);
|
||||||
|
audio += read_int16(in);
|
||||||
|
audio /= 2;
|
||||||
|
}
|
||||||
|
if (pcm_mode) {
|
||||||
|
print_byte(out, audio);
|
||||||
|
print_byte(out, audio >> 8);
|
||||||
|
} else {
|
||||||
|
print_byte(out, ulaw_encode(audio));
|
||||||
|
}
|
||||||
|
length--;
|
||||||
|
}
|
||||||
|
while (padlength > 0) {
|
||||||
|
print_byte(out, 0);
|
||||||
|
padlength--;
|
||||||
|
}
|
||||||
|
while (bcount > 0) {
|
||||||
|
print_byte(out, 0);
|
||||||
|
}
|
||||||
|
if (wcount > 0) fprintf(out, "\n");
|
||||||
|
fprintf(out, "};\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t ulaw_encode(int16_t audio)
|
||||||
|
{
|
||||||
|
uint32_t mag, neg;
|
||||||
|
|
||||||
|
// http://en.wikipedia.org/wiki/G.711
|
||||||
|
if (audio >= 0) {
|
||||||
|
mag = audio;
|
||||||
|
neg = 0;
|
||||||
|
} else {
|
||||||
|
mag = audio * -1;
|
||||||
|
neg = 0x80;
|
||||||
|
}
|
||||||
|
mag += 128;
|
||||||
|
if (mag > 0x7FFF) mag = 0x7FFF;
|
||||||
|
if (mag >= 0x4000) return neg | 0x70 | ((mag >> 10) & 0x0F); // 01wx yz00 0000 0000
|
||||||
|
if (mag >= 0x2000) return neg | 0x60 | ((mag >> 9) & 0x0F); // 001w xyz0 0000 0000
|
||||||
|
if (mag >= 0x1000) return neg | 0x50 | ((mag >> 8) & 0x0F); // 0001 wxyz 0000 0000
|
||||||
|
if (mag >= 0x0800) return neg | 0x40 | ((mag >> 7) & 0x0F); // 0000 1wxy z000 0000
|
||||||
|
if (mag >= 0x0400) return neg | 0x30 | ((mag >> 6) & 0x0F); // 0000 01wx yz00 0000
|
||||||
|
if (mag >= 0x0200) return neg | 0x20 | ((mag >> 5) & 0x0F); // 0000 001w xyz0 0000
|
||||||
|
if (mag >= 0x0100) return neg | 0x10 | ((mag >> 4) & 0x0F); // 0000 0001 wxyz 0000
|
||||||
|
return neg | 0x00 | ((mag >> 3) & 0x0F); // 0000 0000 1wxy z000
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// compute the extra padding needed
|
||||||
|
uint32_t padding(uint32_t length, uint32_t block)
|
||||||
|
{
|
||||||
|
uint32_t extra;
|
||||||
|
|
||||||
|
extra = length % block;
|
||||||
|
if (extra == 0) return 0;
|
||||||
|
return block - extra;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack the output bytes into 32 bit words, lsb first, and
|
||||||
|
// format the data nicely with commas and newlines
|
||||||
|
void print_byte(FILE *out, uint8_t b)
|
||||||
|
{
|
||||||
|
static uint32_t buf32=0;
|
||||||
|
|
||||||
|
buf32 |= (b << (8 * bcount++));
|
||||||
|
if (bcount >= 4) {
|
||||||
|
fprintf(out, "0x%08X,", buf32);
|
||||||
|
buf32 = 0;
|
||||||
|
bcount = 0;
|
||||||
|
if (++wcount >= 8) {
|
||||||
|
fprintf(out, "\n");
|
||||||
|
wcount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the WAV filename into a C-compatible name
|
||||||
|
void filename2samplename(void)
|
||||||
|
{
|
||||||
|
int len, i, n;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
len = strlen(filename) - 4;
|
||||||
|
if (len >= sizeof(samplename)-1) len = sizeof(samplename)-1;
|
||||||
|
for (i=0, n=0; n < len; i++) {
|
||||||
|
c = filename[i];
|
||||||
|
if (isalpha(c) || c == '_' || (isdigit(c) && n > 0)) {
|
||||||
|
samplename[n] = (n == 0) ? toupper(c) : tolower(c);
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
samplename[n] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char *title = "// Audio data converted from WAV file by wav2sketch\n\n";
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
DIR *dir;
|
||||||
|
struct dirent *f;
|
||||||
|
struct stat s;
|
||||||
|
FILE *fp, *outc=NULL, *outh=NULL;
|
||||||
|
char buf[128];
|
||||||
|
int i, len;
|
||||||
|
|
||||||
|
// By default, audio is u-law encoded to reduce the memory requirement
|
||||||
|
// in half. However, u-law does add distortion. If "-16" is specified
|
||||||
|
// on the command line, the original 16 bit PCM samples are used.
|
||||||
|
for (i=1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-16") == 0) pcm_mode = 1;
|
||||||
|
}
|
||||||
|
dir = opendir(".");
|
||||||
|
if (!dir) die("unable to open directory");
|
||||||
|
while (1) {
|
||||||
|
f = readdir(dir);
|
||||||
|
if (!f) break;
|
||||||
|
//if ((f->d_type & DT_DIR)) continue; // skip directories
|
||||||
|
//if (!(f->d_type & DT_REG)) continue; // skip special files
|
||||||
|
if (stat(f->d_name, &s) < 0) continue; // skip if unable to stat
|
||||||
|
if (S_ISDIR(s.st_mode)) continue; // skip directories
|
||||||
|
if (!S_ISREG(s.st_mode)) continue; // skip special files
|
||||||
|
filename = f->d_name;
|
||||||
|
len = strlen(filename);
|
||||||
|
if (len < 5) continue;
|
||||||
|
if (strcasecmp(filename + len - 4, ".wav") != 0) continue;
|
||||||
|
fp = fopen(filename, "rb");
|
||||||
|
if (!fp) die("unable to read file %s", filename);
|
||||||
|
filename2samplename();
|
||||||
|
printf("converting: %s --> AudioSample%s\n", filename, samplename);
|
||||||
|
snprintf(buf, sizeof(buf), "AudioSample%s.cpp", samplename);
|
||||||
|
outc = fopen(buf, "w");
|
||||||
|
if (outc == NULL) die("unable to write %s", buf);
|
||||||
|
snprintf(buf, sizeof(buf), "AudioSample%s.h", samplename);
|
||||||
|
outh = fopen(buf, "w");
|
||||||
|
if (outh == NULL) die("unable to write %s\n", buf);
|
||||||
|
fprintf(outh, "%s", title);
|
||||||
|
fprintf(outc, "%s", title);
|
||||||
|
fprintf(outc, "#include \"%s\"\n\n", buf);
|
||||||
|
wav2c(fp, outc, outh);
|
||||||
|
//wav2c(fp, stdout, stdout);
|
||||||
|
fclose(outc);
|
||||||
|
fclose(outh);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
printf("Total data size %d bytes\n", total_length * 4);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t read_uint8(FILE *in)
|
||||||
|
{
|
||||||
|
int c1;
|
||||||
|
|
||||||
|
c1 = fgetc(in);
|
||||||
|
if (c1 == EOF) die("error, end of data while reading from %s\n", filename);
|
||||||
|
c1 &= 255;
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t read_int16(FILE *in)
|
||||||
|
{
|
||||||
|
int c1, c2;
|
||||||
|
|
||||||
|
c1 = fgetc(in);
|
||||||
|
if (c1 == EOF) die("error, end of data while reading from %s\n", filename);
|
||||||
|
c2 = fgetc(in);
|
||||||
|
if (c2 == EOF) die("error, end of data while reading from %s\n", filename);
|
||||||
|
c1 &= 255;
|
||||||
|
c2 &= 255;
|
||||||
|
return (c2 << 8) | c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t read_uint32(FILE *in)
|
||||||
|
{
|
||||||
|
int c1, c2, c3, c4;
|
||||||
|
|
||||||
|
c1 = fgetc(in);
|
||||||
|
if (c1 == EOF) die("error, end of data while reading from %s\n", filename);
|
||||||
|
c2 = fgetc(in);
|
||||||
|
if (c2 == EOF) die("error, end of data while reading from %s\n", filename);
|
||||||
|
c3 = fgetc(in);
|
||||||
|
if (c3 == EOF) die("error, end of data while reading from %s\n", filename);
|
||||||
|
c4 = fgetc(in);
|
||||||
|
if (c4 == EOF) die("error, end of data while reading from %s\n", filename);
|
||||||
|
c1 &= 255;
|
||||||
|
c2 &= 255;
|
||||||
|
c3 &= 255;
|
||||||
|
c4 &= 255;
|
||||||
|
return (c4 << 24) | (c3 << 16) | (c2 << 8) | c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void die(const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
fprintf(stderr, "wav2sketch: ");
|
||||||
|
vfprintf(stderr, format, args);
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
#include "filter_biquad.h"
|
#include "filter_biquad.h"
|
||||||
#include "utility/dspinst.h"
|
#include "utility/dspinst.h"
|
||||||
|
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
|
|
||||||
void AudioFilterBiquad::update(void)
|
void AudioFilterBiquad::update(void)
|
||||||
{
|
{
|
||||||
|
|
@ -68,6 +68,11 @@ void AudioFilterBiquad::update(void)
|
||||||
bprev = in2;
|
bprev = in2;
|
||||||
aprev = pack_16b_16b(
|
aprev = pack_16b_16b(
|
||||||
signed_saturate_rshift(sum, 16, 14), out2);
|
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;
|
sum &= 0x3FFF;
|
||||||
bprev = in2;
|
bprev = in2;
|
||||||
*data++ = aprev;
|
*data++ = aprev;
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@
|
||||||
// no audible difference.
|
// no audible difference.
|
||||||
//#define IMPROVE_EXPONENTIAL_ACCURACY
|
//#define IMPROVE_EXPONENTIAL_ACCURACY
|
||||||
|
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
|
|
||||||
void AudioFilterStateVariable::update_fixed(const int16_t *in,
|
void AudioFilterStateVariable::update_fixed(const int16_t *in,
|
||||||
int16_t *lp, int16_t *bp, int16_t *hp)
|
int16_t *lp, int16_t *bp, int16_t *hp)
|
||||||
|
|
@ -120,7 +120,7 @@ void AudioFilterStateVariable::update_variable(const int16_t *in,
|
||||||
n = n << 1;
|
n = n << 1;
|
||||||
#else
|
#else
|
||||||
// exp2 algorithm by Laurent de Soras
|
// 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 = (n + 134217728) << 3;
|
||||||
n = multiply_32x32_rshift32_rounded(n, n);
|
n = multiply_32x32_rshift32_rounded(n, n);
|
||||||
n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
|
n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !defined(__IMXRT1052__) && !defined(__IMXRT1062__)
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "input_adc.h"
|
#include "input_adc.h"
|
||||||
#include "utility/pdb.h"
|
#include "utility/pdb.h"
|
||||||
|
|
@ -211,3 +213,4 @@ void AudioInputAnalog::update(void)
|
||||||
transmit(out_left);
|
transmit(out_left);
|
||||||
release(out_left);
|
release(out_left);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
@ -24,11 +24,13 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "input_i2s.h"
|
#include "input_i2s.h"
|
||||||
#include "output_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_left = NULL;
|
||||||
audio_block_t * AudioInputI2S::block_right = NULL;
|
audio_block_t * AudioInputI2S::block_right = NULL;
|
||||||
uint16_t AudioInputI2S::block_offset = 0;
|
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?
|
// TODO: should we set & clear the I2S_RCSR_SR bit here?
|
||||||
AudioOutputI2S::config_i2s();
|
AudioOutputI2S::config_i2s();
|
||||||
|
|
||||||
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
|
|
||||||
#if defined(KINETISK)
|
#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->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 2);
|
||||||
dma.TCD->SOFF = 0;
|
dma.TCD->SOFF = 0;
|
||||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
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->DLASTSGA = -sizeof(i2s_rx_buffer);
|
||||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
|
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
|
||||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
#endif
|
|
||||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
|
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_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
|
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);
|
dma.attachInterrupt(isr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,11 +97,11 @@ void AudioInputI2S::isr(void)
|
||||||
int16_t *dest_left, *dest_right;
|
int16_t *dest_left, *dest_right;
|
||||||
audio_block_t *left, *right;
|
audio_block_t *left, *right;
|
||||||
|
|
||||||
//digitalWriteFast(3, HIGH);
|
#if defined(KINETISK) || defined(__IMXRT1062__)
|
||||||
#if defined(KINETISK)
|
|
||||||
daddr = (uint32_t)(dma.TCD->DADDR);
|
daddr = (uint32_t)(dma.TCD->DADDR);
|
||||||
#endif
|
#endif
|
||||||
dma.clearInterrupt();
|
dma.clearInterrupt();
|
||||||
|
//Serial.println("isr");
|
||||||
|
|
||||||
if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) {
|
if (daddr < (uint32_t)i2s_rx_buffer + sizeof(i2s_rx_buffer) / 2) {
|
||||||
// DMA is receiving to the first half of the buffer
|
// DMA is receiving to the first half of the buffer
|
||||||
|
|
@ -102,7 +123,9 @@ void AudioInputI2S::isr(void)
|
||||||
dest_left = &(left->data[offset]);
|
dest_left = &(left->data[offset]);
|
||||||
dest_right = &(right->data[offset]);
|
dest_right = &(right->data[offset]);
|
||||||
AudioInputI2S::block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
|
AudioInputI2S::block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
//Serial.println(*src);
|
||||||
//n = *src++;
|
//n = *src++;
|
||||||
//*dest_left++ = (int16_t)n;
|
//*dest_left++ = (int16_t)n;
|
||||||
//*dest_right++ = (int16_t)(n >> 16);
|
//*dest_right++ = (int16_t)(n >> 16);
|
||||||
|
|
@ -111,7 +134,6 @@ void AudioInputI2S::isr(void)
|
||||||
} while (src < end);
|
} while (src < end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//digitalWriteFast(3, LOW);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -180,9 +202,9 @@ void AudioInputI2Sslave::begin(void)
|
||||||
//block_right_1st = NULL;
|
//block_right_1st = NULL;
|
||||||
|
|
||||||
AudioOutputI2Sslave::config_i2s();
|
AudioOutputI2Sslave::config_i2s();
|
||||||
|
|
||||||
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
|
|
||||||
#if defined(KINETISK)
|
#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->SADDR = (void *)((uint32_t)&I2S0_RDR0 + 2);
|
||||||
dma.TCD->SOFF = 0;
|
dma.TCD->SOFF = 0;
|
||||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
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->DLASTSGA = -sizeof(i2s_rx_buffer);
|
||||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
|
dma.TCD->BITER_ELINKNO = sizeof(i2s_rx_buffer) / 2;
|
||||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
#endif
|
|
||||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_RX);
|
||||||
update_responsibility = update_setup();
|
update_responsibility = update_setup();
|
||||||
dma.enable();
|
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_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
|
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
|
||||||
dma.attachInterrupt(isr);
|
dma.attachInterrupt(isr);
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
213
sampler/lib/Audio_SdFat/input_i2s2.cpp
Normal file
213
sampler/lib/Audio_SdFat/input_i2s2.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__IMXRT1062__)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "input_i2s2.h"
|
||||||
|
#include "output_i2s2.h"
|
||||||
|
|
||||||
|
static uint32_t i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES];
|
||||||
|
audio_block_t * AudioInputI2S2::block_left = NULL;
|
||||||
|
audio_block_t * AudioInputI2S2::block_right = NULL;
|
||||||
|
uint16_t AudioInputI2S2::block_offset = 0;
|
||||||
|
bool AudioInputI2S2::update_responsibility = false;
|
||||||
|
DMAChannel AudioInputI2S2::dma(false);
|
||||||
|
|
||||||
|
|
||||||
|
void AudioInputI2S2::begin(void)
|
||||||
|
{
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
//block_left_1st = NULL;
|
||||||
|
//block_right_1st = NULL;
|
||||||
|
|
||||||
|
// TODO: should we set & clear the I2S_RCSR_SR bit here?
|
||||||
|
AudioOutputI2S2::config_i2s();
|
||||||
|
|
||||||
|
CORE_PIN5_CONFIG = 2; //2:RX_DATA0
|
||||||
|
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0;
|
||||||
|
|
||||||
|
dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0+2);
|
||||||
|
dma.TCD->SOFF = 0;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
||||||
|
dma.TCD->NBYTES_MLNO = 2;
|
||||||
|
dma.TCD->SLAST = 0;
|
||||||
|
dma.TCD->DADDR = i2s2_rx_buffer;
|
||||||
|
dma.TCD->DOFF = 2;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
|
||||||
|
dma.TCD->DLASTSGA = -sizeof(i2s2_rx_buffer);
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX);
|
||||||
|
|
||||||
|
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE;
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE;
|
||||||
|
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.enable();
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
//pinMode(13, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputI2S2::isr(void)
|
||||||
|
{
|
||||||
|
uint32_t daddr, offset;
|
||||||
|
const int16_t *src, *end;
|
||||||
|
int16_t *dest_left, *dest_right;
|
||||||
|
audio_block_t *left, *right;
|
||||||
|
|
||||||
|
//digitalWriteFast(13, HIGH);
|
||||||
|
daddr = (uint32_t)(dma.TCD->DADDR);
|
||||||
|
dma.clearInterrupt();
|
||||||
|
|
||||||
|
if (daddr < (uint32_t)i2s2_rx_buffer + sizeof(i2s2_rx_buffer) / 2) {
|
||||||
|
// DMA is receiving to the first half of the buffer
|
||||||
|
// need to remove data from the second half
|
||||||
|
src = (int16_t *)&i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES/2];
|
||||||
|
end = (int16_t *)&i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES];
|
||||||
|
if (AudioInputI2S2::update_responsibility) AudioStream::update_all();
|
||||||
|
} else {
|
||||||
|
// DMA is receiving to the second half of the buffer
|
||||||
|
// need to remove data from the first half
|
||||||
|
src = (int16_t *)&i2s2_rx_buffer[0];
|
||||||
|
end = (int16_t *)&i2s2_rx_buffer[AUDIO_BLOCK_SAMPLES/2];
|
||||||
|
}
|
||||||
|
left = AudioInputI2S2::block_left;
|
||||||
|
right = AudioInputI2S2::block_right;
|
||||||
|
if (left != NULL && right != NULL) {
|
||||||
|
offset = AudioInputI2S2::block_offset;
|
||||||
|
if (offset <= AUDIO_BLOCK_SAMPLES/2) {
|
||||||
|
dest_left = &(left->data[offset]);
|
||||||
|
dest_right = &(right->data[offset]);
|
||||||
|
AudioInputI2S2::block_offset = offset + AUDIO_BLOCK_SAMPLES/2;
|
||||||
|
|
||||||
|
do {
|
||||||
|
//Serial.println(*src);
|
||||||
|
//n = *src++;
|
||||||
|
//*dest_left++ = (int16_t)n;
|
||||||
|
//*dest_right++ = (int16_t)(n >> 16);
|
||||||
|
*dest_left++ = *src++;
|
||||||
|
*dest_right++ = *src++;
|
||||||
|
} while (src < end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//digitalWriteFast(13, LOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AudioInputI2S2::update(void)
|
||||||
|
{
|
||||||
|
audio_block_t *new_left=NULL, *new_right=NULL, *out_left=NULL, *out_right=NULL;
|
||||||
|
|
||||||
|
// allocate 2 new blocks, but if one fails, allocate neither
|
||||||
|
new_left = allocate();
|
||||||
|
if (new_left != NULL) {
|
||||||
|
new_right = allocate();
|
||||||
|
if (new_right == NULL) {
|
||||||
|
release(new_left);
|
||||||
|
new_left = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__disable_irq();
|
||||||
|
if (block_offset >= AUDIO_BLOCK_SAMPLES) {
|
||||||
|
// the DMA filled 2 blocks, so grab them and get the
|
||||||
|
// 2 new blocks to the DMA, as quickly as possible
|
||||||
|
out_left = block_left;
|
||||||
|
block_left = new_left;
|
||||||
|
out_right = block_right;
|
||||||
|
block_right = new_right;
|
||||||
|
block_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
// then transmit the DMA's former blocks
|
||||||
|
transmit(out_left, 0);
|
||||||
|
release(out_left);
|
||||||
|
transmit(out_right, 1);
|
||||||
|
release(out_right);
|
||||||
|
//Serial.print(".");
|
||||||
|
} else if (new_left != NULL) {
|
||||||
|
// the DMA didn't fill blocks, but we allocated blocks
|
||||||
|
if (block_left == NULL) {
|
||||||
|
// the DMA doesn't have any blocks to fill, so
|
||||||
|
// give it the ones we just allocated
|
||||||
|
block_left = new_left;
|
||||||
|
block_right = new_right;
|
||||||
|
block_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
// the DMA already has blocks, doesn't need these
|
||||||
|
__enable_irq();
|
||||||
|
release(new_left);
|
||||||
|
release(new_right);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The DMA didn't fill blocks, and we could not allocate
|
||||||
|
// memory... the system is likely starving for memory!
|
||||||
|
// Sadly, there's nothing we can do.
|
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
void AudioInputI2S2slave::begin(void)
|
||||||
|
{
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
//block_left_1st = NULL;
|
||||||
|
//block_right_1st = NULL;
|
||||||
|
|
||||||
|
AudioOutputI2S2slave::config_i2s();
|
||||||
|
|
||||||
|
CORE_PIN33_CONFIG = 2; //2:RX_DATA0
|
||||||
|
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0;
|
||||||
|
|
||||||
|
dma.TCD->SADDR = (void *)((uint32_t)&I2S2_RDR0 + 2);
|
||||||
|
dma.TCD->SOFF = 0;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
||||||
|
dma.TCD->NBYTES_MLNO = 2;
|
||||||
|
dma.TCD->SLAST = 0;
|
||||||
|
dma.TCD->DADDR = i2s2_rx_buffer;
|
||||||
|
dma.TCD->DOFF = 2;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
|
||||||
|
dma.TCD->DLASTSGA = -sizeof(i2s2_rx_buffer);
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(i2s2_rx_buffer) / 2;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX);
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.enable();
|
||||||
|
|
||||||
|
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
62
sampler/lib/Audio_SdFat/input_i2s2.h
Normal file
62
sampler/lib/Audio_SdFat/input_i2s2.h
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
#ifndef _input_i2s2_h_
|
||||||
|
#define _input_i2s2_h_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "AudioStream.h"
|
||||||
|
#include "DMAChannel.h"
|
||||||
|
|
||||||
|
class AudioInputI2S2 : public AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioInputI2S2(void) : AudioStream(0, NULL) { begin(); }
|
||||||
|
virtual void update(void);
|
||||||
|
void begin(void);
|
||||||
|
protected:
|
||||||
|
AudioInputI2S2(int dummy): AudioStream(0, NULL) {} // to be used only inside AudioInputI2Sslave !!
|
||||||
|
static bool update_responsibility;
|
||||||
|
static DMAChannel dma;
|
||||||
|
static void isr(void);
|
||||||
|
private:
|
||||||
|
static audio_block_t *block_left;
|
||||||
|
static audio_block_t *block_right;
|
||||||
|
static uint16_t block_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AudioInputI2S2slave : public AudioInputI2S2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioInputI2S2slave(void) : AudioInputI2S2(0) { begin(); }
|
||||||
|
void begin(void);
|
||||||
|
friend void dma_ch1_isr(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif //#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
|
@ -24,6 +24,8 @@
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#if !defined(__IMXRT1052__) && !defined(__IMXRT1062__)
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "input_pdm.h"
|
#include "input_pdm.h"
|
||||||
#include "utility/dspinst.h"
|
#include "utility/dspinst.h"
|
||||||
|
|
@ -82,12 +84,17 @@ DMAChannel AudioInputPDM::dma(false);
|
||||||
#define MCLK_MULT 1
|
#define MCLK_MULT 1
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
#elif F_CPU == 216000000
|
#elif F_CPU == 216000000
|
||||||
#define MCLK_MULT 8
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 153
|
#define MCLK_DIV 17
|
||||||
#define MCLK_SRC 0
|
#define MCLK_SRC 1
|
||||||
#elif F_CPU == 240000000
|
#elif F_CPU == 240000000
|
||||||
#define MCLK_MULT 4
|
#define MCLK_MULT 2
|
||||||
#define MCLK_DIV 85
|
#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
|
#elif F_CPU == 16000000
|
||||||
#define MCLK_MULT 12
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
|
|
@ -1712,3 +1719,4 @@ for ($n=0; $n < 512; $n += 8) {
|
||||||
print "\n};\n";
|
print "\n};\n";
|
||||||
print "// max=$max, min=$min\n";
|
print "// max=$max, min=$min\n";
|
||||||
*/
|
*/
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,14 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "input_tdm.h"
|
#include "input_tdm.h"
|
||||||
#include "output_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] = {
|
audio_block_t * AudioInputTDM::block_incoming[16] = {
|
||||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||||
};
|
};
|
||||||
bool AudioInputTDM::update_responsibility = false;
|
bool AudioInputTDM::update_responsibility = false;
|
||||||
DMAChannel AudioInputTDM::dma(false);
|
DMAChannel AudioInputTDM::dma(false);
|
||||||
|
|
@ -44,7 +46,7 @@ void AudioInputTDM::begin(void)
|
||||||
|
|
||||||
// TODO: should we set & clear the I2S_RCSR_SR bit here?
|
// TODO: should we set & clear the I2S_RCSR_SR bit here?
|
||||||
AudioOutputTDM::config_tdm();
|
AudioOutputTDM::config_tdm();
|
||||||
|
#if defined(KINETISK)
|
||||||
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
|
CORE_PIN13_CONFIG = PORT_PCR_MUX(4); // pin 13, PTC5, I2S0_RXD0
|
||||||
dma.TCD->SADDR = &I2S0_RDR0;
|
dma.TCD->SADDR = &I2S0_RDR0;
|
||||||
dma.TCD->SOFF = 0;
|
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_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
|
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE; // TX clock enable, because sync'd to TX
|
||||||
dma.attachInterrupt(isr);
|
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...
|
// TODO: needs optimization...
|
||||||
|
|
@ -86,7 +109,6 @@ void AudioInputTDM::isr(void)
|
||||||
const uint32_t *src;
|
const uint32_t *src;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
//digitalWriteFast(3, HIGH);
|
|
||||||
daddr = (uint32_t)(dma.TCD->DADDR);
|
daddr = (uint32_t)(dma.TCD->DADDR);
|
||||||
dma.clearInterrupt();
|
dma.clearInterrupt();
|
||||||
|
|
||||||
|
|
@ -99,7 +121,10 @@ void AudioInputTDM::isr(void)
|
||||||
// need to remove data from the first half
|
// need to remove data from the first half
|
||||||
src = &tdm_rx_buffer[0];
|
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) {
|
for (i=0; i < 16; i += 2) {
|
||||||
uint32_t *dest1 = (uint32_t *)(block_incoming[i]->data);
|
uint32_t *dest1 = (uint32_t *)(block_incoming[i]->data);
|
||||||
uint32_t *dest2 = (uint32_t *)(block_incoming[i+1]->data);
|
uint32_t *dest2 = (uint32_t *)(block_incoming[i+1]->data);
|
||||||
|
|
@ -108,7 +133,6 @@ void AudioInputTDM::isr(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (update_responsibility) update_all();
|
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
|
// allocate 16 new blocks. If any fails, allocate none
|
||||||
for (i=0; i < 16; i++) {
|
for (i=0; i < 16; i++) {
|
||||||
new_block[i] = allocate();
|
new_block[i] = allocate();
|
||||||
if (new_block[i] == NULL) {
|
if (new_block[i] == nullptr) {
|
||||||
for (j=0; j < i; j++) {
|
for (j=0; j < i; j++) {
|
||||||
release(new_block[j]);
|
release(new_block[j]);
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +157,7 @@ void AudioInputTDM::update(void)
|
||||||
memcpy(out_block, block_incoming, sizeof(out_block));
|
memcpy(out_block, block_incoming, sizeof(out_block));
|
||||||
memcpy(block_incoming, new_block, sizeof(block_incoming));
|
memcpy(block_incoming, new_block, sizeof(block_incoming));
|
||||||
__enable_irq();
|
__enable_irq();
|
||||||
if (out_block[0] != NULL) {
|
if (out_block[0] != nullptr) {
|
||||||
// if we got 1 block, all 16 are filled
|
// if we got 1 block, all 16 are filled
|
||||||
for (i=0; i < 16; i++) {
|
for (i=0; i < 16; i++) {
|
||||||
transmit(out_block[i], i);
|
transmit(out_block[i], i);
|
||||||
|
|
@ -143,4 +167,4 @@ void AudioInputTDM::update(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // KINETISK
|
#endif
|
||||||
|
|
|
||||||
151
sampler/lib/Audio_SdFat/input_tdm2.cpp
Normal file
151
sampler/lib/Audio_SdFat/input_tdm2.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1062__)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "input_tdm2.h"
|
||||||
|
#include "output_tdm2.h"
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
|
DMAMEM __attribute__((aligned(32)))
|
||||||
|
static uint32_t tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*16];
|
||||||
|
audio_block_t * AudioInputTDM2::block_incoming[16] = {
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||||
|
};
|
||||||
|
bool AudioInputTDM2::update_responsibility = false;
|
||||||
|
DMAChannel AudioInputTDM2::dma(false);
|
||||||
|
|
||||||
|
|
||||||
|
void AudioInputTDM2::begin(void)
|
||||||
|
{
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
// TODO: should we set & clear the I2S_RCSR_SR bit here?
|
||||||
|
AudioOutputTDM2::config_tdm();
|
||||||
|
|
||||||
|
CORE_PIN5_CONFIG = 2; //2:RX_DATA0
|
||||||
|
IOMUXC_SAI2_RX_DATA0_SELECT_INPUT = 0;
|
||||||
|
dma.TCD->SADDR = &I2S2_RDR0;
|
||||||
|
dma.TCD->SOFF = 0;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
|
||||||
|
dma.TCD->NBYTES_MLNO = 4;
|
||||||
|
dma.TCD->SLAST = 0;
|
||||||
|
dma.TCD->DADDR = tdm_rx_buffer;
|
||||||
|
dma.TCD->DOFF = 4;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
|
||||||
|
dma.TCD->DLASTSGA = -sizeof(tdm_rx_buffer);
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(tdm_rx_buffer) / 4;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_RX);
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.enable();
|
||||||
|
|
||||||
|
I2S2_RCSR |= I2S_RCSR_RE | I2S_RCSR_BCE | I2S_RCSR_FRDE | I2S_RCSR_FR;
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE;
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: needs optimization...
|
||||||
|
static void memcpy_tdm_rx(uint32_t *dest1, uint32_t *dest2, const uint32_t *src)
|
||||||
|
{
|
||||||
|
uint32_t i, in1, in2;
|
||||||
|
|
||||||
|
for (i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
|
||||||
|
in1 = *src;
|
||||||
|
in2 = *(src+8);
|
||||||
|
src += 16;
|
||||||
|
*dest1++ = (in1 >> 16) | (in2 & 0xFFFF0000);
|
||||||
|
*dest2++ = (in1 << 16) | (in2 & 0x0000FFFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioInputTDM2::isr(void)
|
||||||
|
{
|
||||||
|
uint32_t daddr;
|
||||||
|
const uint32_t *src;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
daddr = (uint32_t)(dma.TCD->DADDR);
|
||||||
|
dma.clearInterrupt();
|
||||||
|
|
||||||
|
if (daddr < (uint32_t)tdm_rx_buffer + sizeof(tdm_rx_buffer) / 2) {
|
||||||
|
// DMA is receiving to the first half of the buffer
|
||||||
|
// need to remove data from the second half
|
||||||
|
src = &tdm_rx_buffer[AUDIO_BLOCK_SAMPLES*8];
|
||||||
|
} else {
|
||||||
|
// DMA is receiving to the second half of the buffer
|
||||||
|
// need to remove data from the first half
|
||||||
|
src = &tdm_rx_buffer[0];
|
||||||
|
}
|
||||||
|
if (block_incoming[0] != nullptr) {
|
||||||
|
#if IMXRT_CACHE_ENABLED >=1
|
||||||
|
arm_dcache_delete((void*)src, sizeof(tdm_rx_buffer) / 2);
|
||||||
|
#endif
|
||||||
|
for (i=0; i < 16; i += 2) {
|
||||||
|
uint32_t *dest1 = (uint32_t *)(block_incoming[i]->data);
|
||||||
|
uint32_t *dest2 = (uint32_t *)(block_incoming[i+1]->data);
|
||||||
|
memcpy_tdm_rx(dest1, dest2, src);
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (update_responsibility) update_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AudioInputTDM2::update(void)
|
||||||
|
{
|
||||||
|
unsigned int i, j;
|
||||||
|
audio_block_t *new_block[16];
|
||||||
|
audio_block_t *out_block[16];
|
||||||
|
|
||||||
|
// allocate 16 new blocks. If any fails, allocate none
|
||||||
|
for (i=0; i < 16; i++) {
|
||||||
|
new_block[i] = allocate();
|
||||||
|
if (new_block[i] == nullptr) {
|
||||||
|
for (j=0; j < i; j++) {
|
||||||
|
release(new_block[j]);
|
||||||
|
}
|
||||||
|
memset(new_block, 0, sizeof(new_block));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__disable_irq();
|
||||||
|
memcpy(out_block, block_incoming, sizeof(out_block));
|
||||||
|
memcpy(block_incoming, new_block, sizeof(block_incoming));
|
||||||
|
__enable_irq();
|
||||||
|
if (out_block[0] != nullptr) {
|
||||||
|
// if we got 1 block, all 16 are filled
|
||||||
|
for (i=0; i < 16; i++) {
|
||||||
|
transmit(out_block[i], i);
|
||||||
|
release(out_block[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
50
sampler/lib/Audio_SdFat/input_tdm2.h
Normal file
50
sampler/lib/Audio_SdFat/input_tdm2.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
#ifndef _input_tdm2_h_
|
||||||
|
#define _input_tdm2_h_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "AudioStream.h"
|
||||||
|
#include "DMAChannel.h"
|
||||||
|
|
||||||
|
class AudioInputTDM2 : public AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioInputTDM2(void) : AudioStream(0, NULL) { begin(); }
|
||||||
|
virtual void update(void);
|
||||||
|
void begin(void);
|
||||||
|
protected:
|
||||||
|
static bool update_responsibility;
|
||||||
|
static DMAChannel dma;
|
||||||
|
static void isr(void);
|
||||||
|
private:
|
||||||
|
static audio_block_t *block_incoming[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
@ -1,19 +1,27 @@
|
||||||
Audio KEYWORD2
|
Audio KEYWORD2
|
||||||
AudioConnection KEYWORD2
|
AudioConnection KEYWORD2
|
||||||
AudioInputI2S KEYWORD2
|
AudioInputI2S KEYWORD2
|
||||||
|
AudioInputI2S2 KEYWORD2
|
||||||
AudioInputI2SQuad KEYWORD2
|
AudioInputI2SQuad KEYWORD2
|
||||||
AudioInputI2Sslave KEYWORD2
|
AudioInputI2Sslave KEYWORD2
|
||||||
AudioInputTDM KEYWORD2
|
AudioInputTDM KEYWORD2
|
||||||
|
AudioInputTDM2 KEYWORD2
|
||||||
AudioInputPDM KEYWORD2
|
AudioInputPDM KEYWORD2
|
||||||
AudioInputUSB KEYWORD2
|
AudioInputUSB KEYWORD2
|
||||||
AudioOutputI2S KEYWORD2
|
AudioOutputI2S KEYWORD2
|
||||||
|
AudioOutputI2S2 KEYWORD2
|
||||||
AudioOutputI2SQuad KEYWORD2
|
AudioOutputI2SQuad KEYWORD2
|
||||||
AudioOutputI2Sslave KEYWORD2
|
AudioOutputI2Sslave KEYWORD2
|
||||||
AudioOutputSPDIF KEYWORD2
|
AudioOutputSPDIF KEYWORD2
|
||||||
|
AudioOutputSPDIF2 KEYWORD2
|
||||||
|
AudioOutputSPDIF3 KEYWORD2
|
||||||
AudioOutputPT8211 KEYWORD2
|
AudioOutputPT8211 KEYWORD2
|
||||||
|
AudioOutputPT8211_2 KEYWORD2
|
||||||
AudioOutputTDM KEYWORD2
|
AudioOutputTDM KEYWORD2
|
||||||
|
AudioOutputTDM2 KEYWORD2
|
||||||
AudioOutputADAT KEYWORD2
|
AudioOutputADAT KEYWORD2
|
||||||
AudioOutputPWM KEYWORD2
|
AudioOutputPWM KEYWORD2
|
||||||
|
AudioOutputMQS KEYWORD2
|
||||||
AudioOutputUSB KEYWORD2
|
AudioOutputUSB KEYWORD2
|
||||||
AudioControlSGTL5000 KEYWORD2
|
AudioControlSGTL5000 KEYWORD2
|
||||||
AudioControlWM8731 KEYWORD2
|
AudioControlWM8731 KEYWORD2
|
||||||
|
|
@ -90,6 +98,8 @@ setLowpass KEYWORD2
|
||||||
setHighpass KEYWORD2
|
setHighpass KEYWORD2
|
||||||
setBandpass KEYWORD2
|
setBandpass KEYWORD2
|
||||||
setNotch KEYWORD2
|
setNotch KEYWORD2
|
||||||
|
setLowShelf KEYWORD2
|
||||||
|
setHighShelf KEYWORD2
|
||||||
muteOutput KEYWORD2
|
muteOutput KEYWORD2
|
||||||
unmuteOutput KEYWORD2
|
unmuteOutput KEYWORD2
|
||||||
muteInput KEYWORD2
|
muteInput KEYWORD2
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "Audio_SdFat",
|
"name": "Audio",
|
||||||
"frameworks": "Arduino",
|
"frameworks": "Arduino",
|
||||||
"platforms": "Teensy",
|
"platforms": "Teensy",
|
||||||
"keywords": "sound, audio, FFT, filter, effect",
|
"keywords": "sound, audio, FFT, filter, effect",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
name=Audio_SdFat
|
name=Audio
|
||||||
version=1.3
|
version=1.3
|
||||||
author=Paul Stoffregen
|
author=Paul Stoffregen
|
||||||
maintainer=Paul Stoffregen
|
maintainer=Paul Stoffregen
|
||||||
|
|
@ -7,3 +7,4 @@ paragraph=A toolkit for building streaming audio projects, featuring Polyphonic
|
||||||
category=Signal Input/Output
|
category=Signal Input/Output
|
||||||
url=http://www.pjrc.com/teensy/td_libs_Audio.html
|
url=http://www.pjrc.com/teensy/td_libs_Audio.html
|
||||||
architectures=*
|
architectures=*
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/* Teensyduino Audio Memcpy
|
/* 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
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
|
@ -27,15 +27,13 @@
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
|
#if defined (__ARM_ARCH_7EM__)
|
||||||
|
|
||||||
#include <AudioStream.h>
|
#include <AudioStream.h>
|
||||||
|
|
||||||
.cpu cortex-m4
|
.cpu cortex-m4
|
||||||
.syntax unified
|
.syntax unified
|
||||||
.thumb
|
.thumb
|
||||||
.text
|
.text
|
||||||
.align 2
|
|
||||||
|
|
||||||
/* void memcpy_tointerleave(short *dst, short *srcL, short *srcR); */
|
/* void memcpy_tointerleave(short *dst, short *srcL, short *srcR); */
|
||||||
.global memcpy_tointerleaveLR
|
.global memcpy_tointerleaveLR
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
#include "mixer.h"
|
#include "mixer.h"
|
||||||
#include "utility/dspinst.h"
|
#include "utility/dspinst.h"
|
||||||
|
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
#define MULTI_UNITYGAIN 65536
|
#define MULTI_UNITYGAIN 65536
|
||||||
|
|
||||||
static void applyGain(int16_t *data, int32_t mult)
|
static void applyGain(int16_t *data, int32_t mult)
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@
|
||||||
|
|
||||||
class AudioMixer4 : public AudioStream
|
class AudioMixer4 : public AudioStream
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
public:
|
public:
|
||||||
AudioMixer4(void) : AudioStream(4, inputQueueArray) {
|
AudioMixer4(void) : AudioStream(4, inputQueueArray) {
|
||||||
for (int i=0; i<4; i++) multiplier[i] = 65536;
|
for (int i=0; i<4; i++) multiplier[i] = 65536;
|
||||||
|
|
|
||||||
|
|
@ -665,6 +665,54 @@ void AudioOutputADAT::config_ADAT(void)
|
||||||
#endif
|
#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)
|
#elif defined(KINETISL)
|
||||||
|
|
||||||
|
|
@ -692,46 +740,3 @@ void AudioOutputADAT::update(void)
|
||||||
|
|
||||||
#endif
|
#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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,12 @@ audio_block_t * AudioOutputI2S::block_right_2nd = NULL;
|
||||||
uint16_t AudioOutputI2S::block_left_offset = 0;
|
uint16_t AudioOutputI2S::block_left_offset = 0;
|
||||||
uint16_t AudioOutputI2S::block_right_offset = 0;
|
uint16_t AudioOutputI2S::block_right_offset = 0;
|
||||||
bool AudioOutputI2S::update_responsibility = false;
|
bool AudioOutputI2S::update_responsibility = false;
|
||||||
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
|
|
||||||
DMAChannel AudioOutputI2S::dma(false);
|
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)
|
void AudioOutputI2S::begin(void)
|
||||||
{
|
{
|
||||||
|
|
@ -45,11 +49,11 @@ void AudioOutputI2S::begin(void)
|
||||||
block_left_1st = NULL;
|
block_left_1st = NULL;
|
||||||
block_right_1st = NULL;
|
block_right_1st = NULL;
|
||||||
|
|
||||||
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
|
||||||
config_i2s();
|
config_i2s();
|
||||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
|
||||||
|
|
||||||
#if defined(KINETISK)
|
#if defined(KINETISK)
|
||||||
|
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
||||||
|
|
||||||
dma.TCD->SADDR = i2s_tx_buffer;
|
dma.TCD->SADDR = i2s_tx_buffer;
|
||||||
dma.TCD->SOFF = 2;
|
dma.TCD->SOFF = 2;
|
||||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
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->DLASTSGA = 0;
|
||||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
|
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
|
||||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
#endif
|
|
||||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
|
||||||
update_responsibility = update_setup();
|
|
||||||
dma.enable();
|
dma.enable();
|
||||||
|
|
||||||
I2S0_TCSR = I2S_TCSR_SR;
|
I2S0_TCSR = I2S_TCSR_SR;
|
||||||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
|
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);
|
dma.attachInterrupt(isr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AudioOutputI2S::isr(void)
|
void AudioOutputI2S::isr(void)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined(KINETISK) || defined(__IMXRT1062__)
|
||||||
int16_t *dest;
|
int16_t *dest;
|
||||||
audio_block_t *blockL, *blockR;
|
audio_block_t *blockL, *blockR;
|
||||||
uint32_t saddr, offsetL, offsetR;
|
uint32_t saddr, offsetL, offsetR;
|
||||||
|
|
@ -109,9 +133,10 @@ void AudioOutputI2S::isr(void)
|
||||||
offsetR += AUDIO_BLOCK_SAMPLES / 2;
|
offsetR += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
} else {
|
} else {
|
||||||
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
|
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arm_dcache_flush_delete(dest, sizeof(i2s_tx_buffer) / 2 );
|
||||||
|
|
||||||
if (offsetL < AUDIO_BLOCK_SAMPLES) {
|
if (offsetL < AUDIO_BLOCK_SAMPLES) {
|
||||||
AudioOutputI2S::block_left_offset = offsetL;
|
AudioOutputI2S::block_left_offset = offsetL;
|
||||||
} else {
|
} 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
|
// 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
|
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
|
||||||
|
|
@ -277,12 +302,17 @@ void AudioOutputI2S::update(void)
|
||||||
#define MCLK_MULT 1
|
#define MCLK_MULT 1
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
#elif F_CPU == 216000000
|
#elif F_CPU == 216000000
|
||||||
#define MCLK_MULT 8
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 153
|
#define MCLK_DIV 17
|
||||||
#define MCLK_SRC 0
|
#define MCLK_SRC 1
|
||||||
#elif F_CPU == 240000000
|
#elif F_CPU == 240000000
|
||||||
#define MCLK_MULT 4
|
#define MCLK_MULT 2
|
||||||
#define MCLK_DIV 85
|
#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
|
#elif F_CPU == 16000000
|
||||||
#define MCLK_MULT 12
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
|
|
@ -297,9 +327,12 @@ void AudioOutputI2S::update(void)
|
||||||
#define MCLK_SRC 0 // system clock
|
#define MCLK_SRC 0 // system clock
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void AudioOutputI2S::config_i2s(void)
|
void AudioOutputI2S::config_i2s(void)
|
||||||
{
|
{
|
||||||
|
#if defined(KINETISK) || defined(KINETISL)
|
||||||
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
||||||
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
||||||
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
|
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_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_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_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)
|
void AudioOutputI2Sslave::begin(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
dma.begin(true); // Allocate the DMA channel first
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
//pinMode(2, OUTPUT);
|
//pinMode(2, OUTPUT);
|
||||||
|
|
@ -352,8 +445,9 @@ void AudioOutputI2Sslave::begin(void)
|
||||||
block_right_1st = NULL;
|
block_right_1st = NULL;
|
||||||
|
|
||||||
AudioOutputI2Sslave::config_i2s();
|
AudioOutputI2Sslave::config_i2s();
|
||||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
|
||||||
#if defined(KINETISK)
|
#if defined(KINETISK)
|
||||||
|
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
||||||
dma.TCD->SADDR = i2s_tx_buffer;
|
dma.TCD->SADDR = i2s_tx_buffer;
|
||||||
dma.TCD->SOFF = 2;
|
dma.TCD->SOFF = 2;
|
||||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
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->DLASTSGA = 0;
|
||||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
|
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
|
||||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
#endif
|
|
||||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
|
||||||
update_responsibility = update_setup();
|
|
||||||
dma.enable();
|
|
||||||
|
|
||||||
I2S0_TCSR = I2S_TCSR_SR;
|
I2S0_TCSR = I2S_TCSR_SR;
|
||||||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
|
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);
|
dma.attachInterrupt(isr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioOutputI2Sslave::config_i2s(void)
|
void AudioOutputI2Sslave::config_i2s(void)
|
||||||
{
|
{
|
||||||
|
#if defined(KINETISK)
|
||||||
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
||||||
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
||||||
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
|
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_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_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_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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
314
sampler/lib/Audio_SdFat/output_i2s2.cpp
Normal file
314
sampler/lib/Audio_SdFat/output_i2s2.cpp
Normal file
|
|
@ -0,0 +1,314 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
#if defined(__IMXRT1062__)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "output_i2s2.h"
|
||||||
|
#include "memcpy_audio.h"
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
|
audio_block_t * AudioOutputI2S2::block_left_1st = NULL;
|
||||||
|
audio_block_t * AudioOutputI2S2::block_right_1st = NULL;
|
||||||
|
audio_block_t * AudioOutputI2S2::block_left_2nd = NULL;
|
||||||
|
audio_block_t * AudioOutputI2S2::block_right_2nd = NULL;
|
||||||
|
uint16_t AudioOutputI2S2::block_left_offset = 0;
|
||||||
|
uint16_t AudioOutputI2S2::block_right_offset = 0;
|
||||||
|
bool AudioOutputI2S2::update_responsibility = false;
|
||||||
|
DMAChannel AudioOutputI2S2::dma(false);
|
||||||
|
DMAMEM __attribute__((aligned(32))) static uint32_t i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES];
|
||||||
|
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
|
void AudioOutputI2S2::begin(void)
|
||||||
|
{
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
block_left_1st = NULL;
|
||||||
|
block_right_1st = NULL;
|
||||||
|
|
||||||
|
config_i2s();
|
||||||
|
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
|
||||||
|
|
||||||
|
dma.TCD->SADDR = i2s2_tx_buffer;
|
||||||
|
dma.TCD->SOFF = 2;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
||||||
|
dma.TCD->NBYTES_MLNO = 2;
|
||||||
|
dma.TCD->SLAST = -sizeof(i2s2_tx_buffer);
|
||||||
|
dma.TCD->DOFF = 0;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2;
|
||||||
|
dma.TCD->DLASTSGA = 0;
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2);
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
|
||||||
|
// I2S2_RCSR |= I2S_RCSR_RE;
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
dma.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputI2S2::isr(void)
|
||||||
|
{
|
||||||
|
int16_t *dest;
|
||||||
|
audio_block_t *blockL, *blockR;
|
||||||
|
uint32_t saddr, offsetL, offsetR;
|
||||||
|
|
||||||
|
saddr = (uint32_t)(dma.TCD->SADDR);
|
||||||
|
dma.clearInterrupt();
|
||||||
|
if (saddr < (uint32_t)i2s2_tx_buffer + sizeof(i2s2_tx_buffer) / 2) {
|
||||||
|
// DMA is transmitting the first half of the buffer
|
||||||
|
// so we must fill the second half
|
||||||
|
dest = (int16_t *)&i2s2_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
|
||||||
|
if (AudioOutputI2S2::update_responsibility) AudioStream::update_all();
|
||||||
|
} else {
|
||||||
|
// DMA is transmitting the second half of the buffer
|
||||||
|
// so we must fill the first half
|
||||||
|
dest = (int16_t *)i2s2_tx_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockL = AudioOutputI2S2::block_left_1st;
|
||||||
|
blockR = AudioOutputI2S2::block_right_1st;
|
||||||
|
offsetL = AudioOutputI2S2::block_left_offset;
|
||||||
|
offsetR = AudioOutputI2S2::block_right_offset;
|
||||||
|
|
||||||
|
if (blockL && blockR) {
|
||||||
|
memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
|
||||||
|
offsetL += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
offsetR += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
} else if (blockL) {
|
||||||
|
memcpy_tointerleaveL(dest, blockL->data + offsetL);
|
||||||
|
offsetL += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
} else if (blockR) {
|
||||||
|
memcpy_tointerleaveR(dest, blockR->data + offsetR);
|
||||||
|
offsetR += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
} else {
|
||||||
|
memset(dest,0,AUDIO_BLOCK_SAMPLES * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IMXRT_CACHE_ENABLED >= 2
|
||||||
|
arm_dcache_flush_delete(dest, sizeof(i2s2_tx_buffer) / 2 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (offsetL < AUDIO_BLOCK_SAMPLES) {
|
||||||
|
AudioOutputI2S2::block_left_offset = offsetL;
|
||||||
|
} else {
|
||||||
|
AudioOutputI2S2::block_left_offset = 0;
|
||||||
|
AudioStream::release(blockL);
|
||||||
|
AudioOutputI2S2::block_left_1st = AudioOutputI2S2::block_left_2nd;
|
||||||
|
AudioOutputI2S2::block_left_2nd = NULL;
|
||||||
|
}
|
||||||
|
if (offsetR < AUDIO_BLOCK_SAMPLES) {
|
||||||
|
AudioOutputI2S2::block_right_offset = offsetR;
|
||||||
|
} else {
|
||||||
|
AudioOutputI2S2::block_right_offset = 0;
|
||||||
|
AudioStream::release(blockR);
|
||||||
|
AudioOutputI2S2::block_right_1st = AudioOutputI2S2::block_right_2nd;
|
||||||
|
AudioOutputI2S2::block_right_2nd = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputI2S2::update(void)
|
||||||
|
{
|
||||||
|
// null audio device: discard all incoming data
|
||||||
|
//if (!active) return;
|
||||||
|
//audio_block_t *block = receiveReadOnly();
|
||||||
|
//if (block) release(block);
|
||||||
|
|
||||||
|
audio_block_t *block;
|
||||||
|
block = receiveReadOnly(0); // input 0 = left channel
|
||||||
|
if (block) {
|
||||||
|
__disable_irq();
|
||||||
|
if (block_left_1st == NULL) {
|
||||||
|
block_left_1st = block;
|
||||||
|
block_left_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else if (block_left_2nd == NULL) {
|
||||||
|
block_left_2nd = block;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_left_1st;
|
||||||
|
block_left_1st = block_left_2nd;
|
||||||
|
block_left_2nd = block;
|
||||||
|
block_left_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
release(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block = receiveReadOnly(1); // input 1 = right channel
|
||||||
|
if (block) {
|
||||||
|
__disable_irq();
|
||||||
|
if (block_right_1st == NULL) {
|
||||||
|
block_right_1st = block;
|
||||||
|
block_right_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else if (block_right_2nd == NULL) {
|
||||||
|
block_right_2nd = block;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_right_1st;
|
||||||
|
block_right_1st = block_right_2nd;
|
||||||
|
block_right_2nd = block;
|
||||||
|
block_right_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
release(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputI2S2::config_i2s(void)
|
||||||
|
{
|
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
|
||||||
|
//PLL:
|
||||||
|
int fs = AUDIO_SAMPLE_RATE_EXACT;
|
||||||
|
// PLL between 27*24 = 648MHz und 54*24=1296MHz
|
||||||
|
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
|
||||||
|
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
|
||||||
|
|
||||||
|
double C = ((double)fs * 256 * n1 * n2) / 24000000;
|
||||||
|
int c0 = C;
|
||||||
|
int c2 = 10000;
|
||||||
|
int c1 = C * c2 - (c0 * c2);
|
||||||
|
set_audioClock(c0, c1, c2);
|
||||||
|
|
||||||
|
// clear SAI2_CLK register locations
|
||||||
|
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
|
||||||
|
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
|
||||||
|
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
|
||||||
|
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
|
||||||
|
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
|
||||||
|
|
||||||
|
// if either transmitter or receiver is enabled, do nothing
|
||||||
|
if (I2S2_TCSR & I2S_TCSR_TE) return;
|
||||||
|
if (I2S2_RCSR & I2S_RCSR_RE) return;
|
||||||
|
|
||||||
|
CORE_PIN33_CONFIG = 2; //2:MCLK
|
||||||
|
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
|
||||||
|
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
|
||||||
|
|
||||||
|
int rsync = 1;
|
||||||
|
int tsync = 0;
|
||||||
|
|
||||||
|
I2S2_TMR = 0;
|
||||||
|
//I2S2_TCSR = (1<<25); //Reset
|
||||||
|
I2S2_TCR1 = I2S_TCR1_RFW(1);
|
||||||
|
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP // sync=0; tx is async;
|
||||||
|
| (I2S_TCR2_BCD | I2S_TCR2_DIV((1)) | I2S_TCR2_MSEL(1));
|
||||||
|
I2S2_TCR3 = I2S_TCR3_TCE;
|
||||||
|
I2S2_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((32-1)) | I2S_TCR4_MF | I2S_TCR4_FSD | I2S_TCR4_FSE | I2S_TCR4_FSP;
|
||||||
|
I2S2_TCR5 = I2S_TCR5_WNW((32-1)) | I2S_TCR5_W0W((32-1)) | I2S_TCR5_FBT((32-1));
|
||||||
|
|
||||||
|
I2S2_RMR = 0;
|
||||||
|
//I2S2_RCSR = (1<<25); //Reset
|
||||||
|
I2S2_RCR1 = I2S_RCR1_RFW(1);
|
||||||
|
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_RCR2_BCP // sync=0; rx is async;
|
||||||
|
| (I2S_RCR2_BCD | I2S_RCR2_DIV((1)) | I2S_RCR2_MSEL(1));
|
||||||
|
I2S2_RCR3 = I2S_RCR3_RCE;
|
||||||
|
I2S2_RCR4 = I2S_RCR4_FRSZ((2-1)) | I2S_RCR4_SYWD((32-1)) | I2S_RCR4_MF | I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
|
||||||
|
I2S2_RCR5 = I2S_RCR5_WNW((32-1)) | I2S_RCR5_W0W((32-1)) | I2S_RCR5_FBT((32-1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
#if 0
|
||||||
|
void AudioOutputI2S2slave::begin(void)
|
||||||
|
{
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
//pinMode(2, OUTPUT);
|
||||||
|
block_left_1st = NULL;
|
||||||
|
block_right_1st = NULL;
|
||||||
|
|
||||||
|
AudioOutputI2S2slave::config_i2s();
|
||||||
|
|
||||||
|
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
|
||||||
|
//CORE_PIN33_CONFIG = 2; //2:RX_DATA0
|
||||||
|
|
||||||
|
dma.TCD->SADDR = i2s2_tx_buffer;
|
||||||
|
dma.TCD->SOFF = 2;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
||||||
|
dma.TCD->NBYTES_MLNO = 2;
|
||||||
|
dma.TCD->SLAST = -sizeof(i2s2_tx_buffer);
|
||||||
|
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0 + 2);
|
||||||
|
dma.TCD->DOFF = 0;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2;
|
||||||
|
dma.TCD->DLASTSGA = 0;
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(i2s2_tx_buffer) / 2;
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
|
||||||
|
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.enable();
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputI2S2slave::config_i2s(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
if (I2S2_TCSR & I2S_TCSR_TE) return;
|
||||||
|
if (I2S2_TCSR & I2S_RCSR_RE) return;
|
||||||
|
|
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
|
||||||
|
/*
|
||||||
|
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
|
||||||
|
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
|
||||||
|
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1) | CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
|
||||||
|
*/
|
||||||
|
// IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK | ((uint32_t)(1<<19)) ))
|
||||||
|
// /*| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR*/ | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0); //Select MCLK
|
||||||
|
|
||||||
|
CORE_PIN5_CONFIG = 2; //2:MCLK
|
||||||
|
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
|
||||||
|
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
|
||||||
|
int rsync = 1;
|
||||||
|
int tsync = 0;
|
||||||
|
|
||||||
|
// configure transmitter
|
||||||
|
I2S2_TMR = 0;
|
||||||
|
I2S2_TCR1 = I2S_TCR1_RFW(1); // watermark at half fifo size
|
||||||
|
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP;
|
||||||
|
I2S2_TCR3 = I2S_TCR3_TCE;
|
||||||
|
I2S2_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(31) | I2S_TCR4_MF
|
||||||
|
| I2S_TCR4_FSE | I2S_TCR4_FSP;
|
||||||
|
I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
|
||||||
|
|
||||||
|
// configure receiver
|
||||||
|
I2S2_TMR = 0;
|
||||||
|
I2S2_TCR1 = I2S_RCR1_RFW(1);
|
||||||
|
I2S2_TCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP;
|
||||||
|
I2S2_TCR3 = I2S_RCR3_RCE;
|
||||||
|
I2S2_TCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(31) | I2S_RCR4_MF
|
||||||
|
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
|
||||||
|
I2S2_TCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif //defined(__IMXRT1062__)
|
||||||
73
sampler/lib/Audio_SdFat/output_i2s2.h
Normal file
73
sampler/lib/Audio_SdFat/output_i2s2.h
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
#ifndef output_i2s2_h_
|
||||||
|
#define output_i2s2_h_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "AudioStream.h"
|
||||||
|
#include "DMAChannel.h"
|
||||||
|
|
||||||
|
|
||||||
|
class AudioOutputI2S2 : public AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutputI2S2(void) : AudioStream(2, inputQueueArray) { begin(); }
|
||||||
|
virtual void update(void);
|
||||||
|
void begin(void);
|
||||||
|
friend class AudioInputI2S2;
|
||||||
|
protected:
|
||||||
|
AudioOutputI2S2(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
|
||||||
|
static void config_i2s(void);
|
||||||
|
static audio_block_t *block_left_1st;
|
||||||
|
static audio_block_t *block_right_1st;
|
||||||
|
static bool update_responsibility;
|
||||||
|
static DMAChannel dma;
|
||||||
|
static void isr(void);
|
||||||
|
private:
|
||||||
|
static audio_block_t *block_left_2nd;
|
||||||
|
static audio_block_t *block_right_2nd;
|
||||||
|
static uint16_t block_left_offset;
|
||||||
|
static uint16_t block_right_offset;
|
||||||
|
audio_block_t *inputQueueArray[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class AudioOutputI2S2slave : public AudioOutputI2S2
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutputI2S2slave(void) : AudioOutputI2S2(0) { begin(); } ;
|
||||||
|
void begin(void);
|
||||||
|
friend class AudioInputI2S2slave;
|
||||||
|
friend void dma_ch0_isr(void);
|
||||||
|
protected:
|
||||||
|
static void config_i2s(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif //defined(__IMXRT1062__)
|
||||||
|
|
@ -272,12 +272,17 @@ void AudioOutputI2SQuad::update(void)
|
||||||
#define MCLK_MULT 1
|
#define MCLK_MULT 1
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
#elif F_CPU == 216000000
|
#elif F_CPU == 216000000
|
||||||
#define MCLK_MULT 8
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 153
|
#define MCLK_DIV 17
|
||||||
#define MCLK_SRC 0
|
#define MCLK_SRC 1
|
||||||
#elif F_CPU == 240000000
|
#elif F_CPU == 240000000
|
||||||
#define MCLK_MULT 4
|
#define MCLK_MULT 2
|
||||||
#define MCLK_DIV 85
|
#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
|
#elif F_CPU == 16000000
|
||||||
#define MCLK_MULT 12
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
|
|
|
||||||
234
sampler/lib/Audio_SdFat/output_mqs.cpp
Normal file
234
sampler/lib/Audio_SdFat/output_mqs.cpp
Normal file
|
|
@ -0,0 +1,234 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
// Frank B
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "output_mqs.h"
|
||||||
|
#include "memcpy_audio.h"
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
|
audio_block_t * AudioOutputMQS::block_left_1st = NULL;
|
||||||
|
audio_block_t * AudioOutputMQS::block_right_1st = NULL;
|
||||||
|
audio_block_t * AudioOutputMQS::block_left_2nd = NULL;
|
||||||
|
audio_block_t * AudioOutputMQS::block_right_2nd = NULL;
|
||||||
|
uint16_t AudioOutputMQS::block_left_offset = 0;
|
||||||
|
uint16_t AudioOutputMQS::block_right_offset = 0;
|
||||||
|
bool AudioOutputMQS::update_responsibility = false;
|
||||||
|
DMAChannel AudioOutputMQS::dma(false);
|
||||||
|
DMAMEM __attribute__((aligned(32)))
|
||||||
|
static uint32_t I2S3_tx_buffer[AUDIO_BLOCK_SAMPLES];
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputMQS::begin(void)
|
||||||
|
{
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
block_left_1st = NULL;
|
||||||
|
block_right_1st = NULL;
|
||||||
|
|
||||||
|
config_i2s();
|
||||||
|
|
||||||
|
CORE_PIN10_CONFIG = 2;//B0_00 MQS_RIGHT
|
||||||
|
CORE_PIN12_CONFIG = 2;//B0_01 MQS_LEFT
|
||||||
|
|
||||||
|
dma.TCD->SADDR = I2S3_tx_buffer;
|
||||||
|
dma.TCD->SOFF = 2;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
||||||
|
dma.TCD->NBYTES_MLNO = 2;
|
||||||
|
dma.TCD->SLAST = -sizeof(I2S3_tx_buffer);
|
||||||
|
dma.TCD->DOFF = 0;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(I2S3_tx_buffer) / 2;
|
||||||
|
dma.TCD->DLASTSGA = 0;
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(I2S3_tx_buffer) / 2;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
dma.TCD->DADDR = (void *)((uint32_t)&I2S3_TDR0 + 0);
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI3_TX);
|
||||||
|
|
||||||
|
I2S3_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
dma.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputMQS::isr(void)
|
||||||
|
{
|
||||||
|
int16_t *dest;
|
||||||
|
audio_block_t *blockL, *blockR;
|
||||||
|
uint32_t saddr, offsetL, offsetR;
|
||||||
|
|
||||||
|
saddr = (uint32_t)(dma.TCD->SADDR);
|
||||||
|
dma.clearInterrupt();
|
||||||
|
if (saddr < (uint32_t)I2S3_tx_buffer + sizeof(I2S3_tx_buffer) / 2) {
|
||||||
|
// DMA is transmitting the first half of the buffer
|
||||||
|
// so we must fill the second half
|
||||||
|
dest = (int16_t *)&I2S3_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
|
||||||
|
if (AudioOutputMQS::update_responsibility) AudioStream::update_all();
|
||||||
|
} else {
|
||||||
|
// DMA is transmitting the second half of the buffer
|
||||||
|
// so we must fill the first half
|
||||||
|
dest = (int16_t *)I2S3_tx_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockL = AudioOutputMQS::block_left_1st;
|
||||||
|
blockR = AudioOutputMQS::block_right_1st;
|
||||||
|
offsetL = AudioOutputMQS::block_left_offset;
|
||||||
|
offsetR = AudioOutputMQS::block_right_offset;
|
||||||
|
|
||||||
|
if (blockL && blockR) {
|
||||||
|
memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
|
||||||
|
offsetL += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
offsetR += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
} else if (blockL) {
|
||||||
|
memcpy_tointerleaveL(dest, blockL->data + offsetL);
|
||||||
|
offsetL += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
} else if (blockR) {
|
||||||
|
memcpy_tointerleaveR(dest, blockR->data + offsetR);
|
||||||
|
offsetR += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
} else {
|
||||||
|
memset(dest,0, sizeof(I2S3_tx_buffer) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IMXRT_CACHE_ENABLED >= 2
|
||||||
|
arm_dcache_flush_delete(dest, sizeof(I2S3_tx_buffer) / 2 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (offsetL < AUDIO_BLOCK_SAMPLES) {
|
||||||
|
AudioOutputMQS::block_left_offset = offsetL;
|
||||||
|
} else {
|
||||||
|
AudioOutputMQS::block_left_offset = 0;
|
||||||
|
AudioStream::release(blockL);
|
||||||
|
AudioOutputMQS::block_left_1st = AudioOutputMQS::block_left_2nd;
|
||||||
|
AudioOutputMQS::block_left_2nd = NULL;
|
||||||
|
}
|
||||||
|
if (offsetR < AUDIO_BLOCK_SAMPLES) {
|
||||||
|
AudioOutputMQS::block_right_offset = offsetR;
|
||||||
|
} else {
|
||||||
|
AudioOutputMQS::block_right_offset = 0;
|
||||||
|
AudioStream::release(blockR);
|
||||||
|
AudioOutputMQS::block_right_1st = AudioOutputMQS::block_right_2nd;
|
||||||
|
AudioOutputMQS::block_right_2nd = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputMQS::update(void)
|
||||||
|
{
|
||||||
|
// null audio device: discard all incoming data
|
||||||
|
//if (!active) return;
|
||||||
|
//audio_block_t *block = receiveReadOnly();
|
||||||
|
//if (block) release(block);
|
||||||
|
//digitalWriteFast(13, LOW);
|
||||||
|
audio_block_t *block;
|
||||||
|
block = receiveReadOnly(0); // input 0 = left channel
|
||||||
|
if (block) {
|
||||||
|
__disable_irq();
|
||||||
|
if (block_left_1st == NULL) {
|
||||||
|
block_left_1st = block;
|
||||||
|
block_left_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else if (block_left_2nd == NULL) {
|
||||||
|
block_left_2nd = block;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_left_1st;
|
||||||
|
block_left_1st = block_left_2nd;
|
||||||
|
block_left_2nd = block;
|
||||||
|
block_left_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
release(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block = receiveReadOnly(1); // input 1 = right channel
|
||||||
|
if (block) {
|
||||||
|
__disable_irq();
|
||||||
|
if (block_right_1st == NULL) {
|
||||||
|
block_right_1st = block;
|
||||||
|
block_right_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else if (block_right_2nd == NULL) {
|
||||||
|
block_right_2nd = block;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_right_1st;
|
||||||
|
block_right_1st = block_right_2nd;
|
||||||
|
block_right_2nd = block;
|
||||||
|
block_right_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
release(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputMQS::config_i2s(void)
|
||||||
|
{
|
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SAI3(CCM_CCGR_ON);
|
||||||
|
CCM_CCGR0 |= CCM_CCGR0_MQS_HMCLK(CCM_CCGR_ON);
|
||||||
|
|
||||||
|
//PLL:
|
||||||
|
//TODO: Check if frequencies are correct!
|
||||||
|
|
||||||
|
int fs = AUDIO_SAMPLE_RATE_EXACT;
|
||||||
|
int oversample = 64*8;
|
||||||
|
// PLL between 27*24 = 648MHz und 54*24=1296MHz
|
||||||
|
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
|
||||||
|
int n2 = 1 + (24000000 * 27) / (fs * oversample * n1);
|
||||||
|
|
||||||
|
double C = ((double)fs * oversample * n1 * n2) / 24000000;
|
||||||
|
int c0 = C;
|
||||||
|
int c2 = 10000;
|
||||||
|
int c1 = C * c2 - (c0 * c2);
|
||||||
|
|
||||||
|
set_audioClock(c0, c1, c2);
|
||||||
|
|
||||||
|
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI3_CLK_SEL_MASK))
|
||||||
|
| CCM_CSCMR1_SAI3_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
|
||||||
|
CCM_CS1CDR = (CCM_CS1CDR & ~(CCM_CS1CDR_SAI3_CLK_PRED_MASK | CCM_CS1CDR_SAI3_CLK_PODF_MASK))
|
||||||
|
| CCM_CS1CDR_SAI3_CLK_PRED(n1-1)
|
||||||
|
| CCM_CS1CDR_SAI3_CLK_PODF(n2-1);
|
||||||
|
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI3_MCLK3_SEL_MASK))
|
||||||
|
| (IOMUXC_GPR_GPR1_SAI3_MCLK_DIR | IOMUXC_GPR_GPR1_SAI3_MCLK3_SEL(0)); //Select MCLK
|
||||||
|
|
||||||
|
IOMUXC_GPR_GPR2 = (IOMUXC_GPR_GPR2 & ~(IOMUXC_GPR_GPR2_MQS_OVERSAMPLE | IOMUXC_GPR_GPR2_MQS_CLK_DIV_MASK))
|
||||||
|
| IOMUXC_GPR_GPR2_MQS_EN | IOMUXC_GPR_GPR2_MQS_OVERSAMPLE | IOMUXC_GPR_GPR2_MQS_CLK_DIV(0);
|
||||||
|
|
||||||
|
if (I2S3_TCSR & I2S_TCSR_TE) return;
|
||||||
|
|
||||||
|
I2S3_TMR = 0;
|
||||||
|
// I2S3_TCSR = (1<<25); //Reset
|
||||||
|
I2S3_TCR1 = I2S_TCR1_RFW(1);
|
||||||
|
I2S3_TCR2 = I2S_TCR2_SYNC(0) /*| I2S_TCR2_BCP*/ // sync=0; tx is async;
|
||||||
|
| (I2S_TCR2_BCD | I2S_TCR2_DIV((7)) | I2S_TCR2_MSEL(1));
|
||||||
|
I2S3_TCR3 = I2S_TCR3_TCE;
|
||||||
|
I2S3_TCR4 = I2S_TCR4_FRSZ((2-1)) | I2S_TCR4_SYWD((16-1)) | I2S_TCR4_MF | I2S_TCR4_FSD /*| I2S_TCR4_FSE*/ /* | I2S_TCR4_FSP */;
|
||||||
|
I2S3_TCR5 = I2S_TCR5_WNW((16-1)) | I2S_TCR5_W0W((16-1)) | I2S_TCR5_FBT((16-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //defined(__IMXRT1062__)
|
||||||
58
sampler/lib/Audio_SdFat/output_mqs.h
Normal file
58
sampler/lib/Audio_SdFat/output_mqs.h
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
#ifndef output_mqs_h_
|
||||||
|
#define output_mqs_h_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "AudioStream.h"
|
||||||
|
#include "DMAChannel.h"
|
||||||
|
|
||||||
|
class AudioOutputMQS : public AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutputMQS(void) : AudioStream(2, inputQueueArray) { begin(); }
|
||||||
|
virtual void update(void);
|
||||||
|
void begin(void);
|
||||||
|
friend class AudioInputI2S2;
|
||||||
|
protected:
|
||||||
|
static void config_i2s(void);
|
||||||
|
static audio_block_t *block_left_1st;
|
||||||
|
static audio_block_t *block_right_1st;
|
||||||
|
static bool update_responsibility;
|
||||||
|
static DMAChannel dma;
|
||||||
|
static void isr(void);
|
||||||
|
private:
|
||||||
|
static audio_block_t *block_left_2nd;
|
||||||
|
static audio_block_t *block_right_2nd;
|
||||||
|
static uint16_t block_left_offset;
|
||||||
|
static uint16_t block_right_offset;
|
||||||
|
audio_block_t *inputQueueArray[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif //defined(__IMXRT1062__)
|
||||||
|
|
@ -26,9 +26,11 @@
|
||||||
|
|
||||||
//Adapted to PT8211, Frank Bösing, Ben-Rheinland
|
//Adapted to PT8211, Frank Bösing, Ben-Rheinland
|
||||||
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "output_pt8211.h"
|
#include "output_pt8211.h"
|
||||||
#include "memcpy_audio.h"
|
#include "memcpy_audio.h"
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
audio_block_t * AudioOutputPT8211::block_left_1st = NULL;
|
audio_block_t * AudioOutputPT8211::block_left_1st = NULL;
|
||||||
audio_block_t * AudioOutputPT8211::block_right_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;
|
uint16_t AudioOutputPT8211::block_right_offset = 0;
|
||||||
bool AudioOutputPT8211::update_responsibility = false;
|
bool AudioOutputPT8211::update_responsibility = false;
|
||||||
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
#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
|
#else
|
||||||
DMAMEM static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
|
static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
|
||||||
#endif
|
#endif
|
||||||
DMAChannel AudioOutputPT8211::dma(false);
|
DMAChannel AudioOutputPT8211::dma(false);
|
||||||
|
|
||||||
|
|
@ -53,9 +55,9 @@ void AudioOutputPT8211::begin(void)
|
||||||
|
|
||||||
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
||||||
config_i2s();
|
config_i2s();
|
||||||
|
#if defined(KINETISK)
|
||||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
||||||
|
|
||||||
#if defined(KINETISK)
|
|
||||||
dma.TCD->SADDR = i2s_tx_buffer;
|
dma.TCD->SADDR = i2s_tx_buffer;
|
||||||
dma.TCD->SOFF = 2;
|
dma.TCD->SOFF = 2;
|
||||||
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
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->DLASTSGA = 0;
|
||||||
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
|
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
|
||||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
#endif
|
|
||||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
|
||||||
update_responsibility = update_setup();
|
update_responsibility = update_setup();
|
||||||
dma.enable();
|
|
||||||
|
|
||||||
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
|
|
||||||
dma.attachInterrupt(isr);
|
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)
|
void AudioOutputPT8211::isr(void)
|
||||||
|
|
@ -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
|
// 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
|
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
|
||||||
|
|
@ -403,12 +434,17 @@ void AudioOutputPT8211::update(void)
|
||||||
#define MCLK_MULT 1
|
#define MCLK_MULT 1
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
#elif F_CPU == 216000000
|
#elif F_CPU == 216000000
|
||||||
#define MCLK_MULT 8
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 153
|
#define MCLK_DIV 17
|
||||||
#define MCLK_SRC 0
|
#define MCLK_SRC 1
|
||||||
#elif F_CPU == 240000000
|
#elif F_CPU == 240000000
|
||||||
#define MCLK_MULT 4
|
#define MCLK_MULT 2
|
||||||
#define MCLK_DIV 85
|
#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
|
#elif F_CPU == 16000000
|
||||||
#define MCLK_MULT 12
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
|
|
@ -423,9 +459,11 @@ void AudioOutputPT8211::update(void)
|
||||||
#define MCLK_SRC 0 // system clock
|
#define MCLK_SRC 0 // system clock
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
void AudioOutputPT8211::config_i2s(void)
|
void AudioOutputPT8211::config_i2s(void)
|
||||||
{
|
{
|
||||||
|
#if defined(KINETISK)
|
||||||
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
||||||
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
||||||
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
|
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
|
||||||
|
|
@ -455,7 +493,65 @@ void AudioOutputPT8211::config_i2s(void)
|
||||||
// configure pin mux for 3 clock signals
|
// configure pin mux for 3 clock signals
|
||||||
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
|
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_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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,9 +45,7 @@ public:
|
||||||
AudioOutputPT8211(void) : AudioStream(2, inputQueueArray) { begin(); }
|
AudioOutputPT8211(void) : AudioStream(2, inputQueueArray) { begin(); }
|
||||||
virtual void update(void);
|
virtual void update(void);
|
||||||
void begin(void);
|
void begin(void);
|
||||||
//friend class AudioInputI2S;
|
|
||||||
protected:
|
protected:
|
||||||
//AudioOutputI2S(int dummy): AudioStream(2, inputQueueArray) {} // to be used only inside AudioOutputI2Sslave !!
|
|
||||||
static void config_i2s(void);
|
static void config_i2s(void);
|
||||||
static audio_block_t *block_left_1st;
|
static audio_block_t *block_left_1st;
|
||||||
static audio_block_t *block_right_1st;
|
static audio_block_t *block_right_1st;
|
||||||
|
|
|
||||||
427
sampler/lib/Audio_SdFat/output_pt8211_2.cpp
Normal file
427
sampler/lib/Audio_SdFat/output_pt8211_2.cpp
Normal file
|
|
@ -0,0 +1,427 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2016, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Adapted to PT8211, Frank Bösing, Ben-Rheinland
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "output_pt8211_2.h"
|
||||||
|
#include "memcpy_audio.h"
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
|
audio_block_t * AudioOutputPT8211_2::block_left_1st = NULL;
|
||||||
|
audio_block_t * AudioOutputPT8211_2::block_right_1st = NULL;
|
||||||
|
audio_block_t * AudioOutputPT8211_2::block_left_2nd = NULL;
|
||||||
|
audio_block_t * AudioOutputPT8211_2::block_right_2nd = NULL;
|
||||||
|
uint16_t AudioOutputPT8211_2::block_left_offset = 0;
|
||||||
|
uint16_t AudioOutputPT8211_2::block_right_offset = 0;
|
||||||
|
bool AudioOutputPT8211_2::update_responsibility = false;
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES*4];
|
||||||
|
#else
|
||||||
|
static uint32_t i2s_tx_buffer[AUDIO_BLOCK_SAMPLES];
|
||||||
|
#endif
|
||||||
|
DMAChannel AudioOutputPT8211_2::dma(false);
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
|
void AudioOutputPT8211_2::begin(void)
|
||||||
|
{
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
block_left_1st = NULL;
|
||||||
|
block_right_1st = NULL;
|
||||||
|
|
||||||
|
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
||||||
|
config_i2s();
|
||||||
|
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
|
||||||
|
|
||||||
|
dma.TCD->SADDR = i2s_tx_buffer;
|
||||||
|
dma.TCD->SOFF = 2;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(1) | DMA_TCD_ATTR_DSIZE(1);
|
||||||
|
dma.TCD->NBYTES_MLNO = 2;
|
||||||
|
dma.TCD->SLAST = -sizeof(i2s_tx_buffer);
|
||||||
|
dma.TCD->DOFF = 0;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
|
||||||
|
dma.TCD->DLASTSGA = 0;
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(i2s_tx_buffer) / 2;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
dma.TCD->DADDR = (void *)((uint32_t)&I2S2_TDR0);
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
|
||||||
|
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
|
||||||
|
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
dma.enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputPT8211_2::isr(void)
|
||||||
|
{
|
||||||
|
int16_t *dest;
|
||||||
|
audio_block_t *blockL, *blockR;
|
||||||
|
uint32_t saddr, offsetL, offsetR;
|
||||||
|
|
||||||
|
saddr = (uint32_t)(dma.TCD->SADDR);
|
||||||
|
dma.clearInterrupt();
|
||||||
|
if (saddr < (uint32_t)i2s_tx_buffer + sizeof(i2s_tx_buffer) / 2) {
|
||||||
|
// DMA is transmitting the first half of the buffer
|
||||||
|
// so we must fill the second half
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
dest = (int16_t *)&i2s_tx_buffer[(AUDIO_BLOCK_SAMPLES/2)*4];
|
||||||
|
#else
|
||||||
|
dest = (int16_t *)&i2s_tx_buffer[AUDIO_BLOCK_SAMPLES/2];
|
||||||
|
#endif
|
||||||
|
if (AudioOutputPT8211_2::update_responsibility) AudioStream::update_all();
|
||||||
|
} else {
|
||||||
|
// DMA is transmitting the second half of the buffer
|
||||||
|
// so we must fill the first half
|
||||||
|
dest = (int16_t *)i2s_tx_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockL = AudioOutputPT8211_2::block_left_1st;
|
||||||
|
blockR = AudioOutputPT8211_2::block_right_1st;
|
||||||
|
offsetL = AudioOutputPT8211_2::block_left_offset;
|
||||||
|
offsetR = AudioOutputPT8211_2::block_right_offset;
|
||||||
|
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
static int32_t oldL = 0;
|
||||||
|
static int32_t oldR = 0;
|
||||||
|
#endif
|
||||||
|
if (blockL && blockR) {
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
#if defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
|
||||||
|
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
|
||||||
|
int32_t valL = blockL->data[offsetL];
|
||||||
|
int32_t valR = blockR->data[offsetR];
|
||||||
|
int32_t nL = (oldL+valL) >> 1;
|
||||||
|
int32_t nR = (oldR+valR) >> 1;
|
||||||
|
*(dest+0) = (oldL+nL) >> 1;
|
||||||
|
*(dest+1) = (oldR+nR) >> 1;
|
||||||
|
*(dest+2) = nL;
|
||||||
|
*(dest+3) = nR;
|
||||||
|
*(dest+4) = (nL+valL) >> 1;
|
||||||
|
*(dest+5) = (nR+valR) >> 1;
|
||||||
|
*(dest+6) = valL;
|
||||||
|
*(dest+7) = valR;
|
||||||
|
dest+=8;
|
||||||
|
oldL = valL;
|
||||||
|
oldR = valR;
|
||||||
|
}
|
||||||
|
#elif defined(AUDIO_PT8211_INTERPOLATION_CIC)
|
||||||
|
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
|
||||||
|
int32_t valL = blockL->data[offsetL];
|
||||||
|
int32_t valR = blockR->data[offsetR];
|
||||||
|
|
||||||
|
int32_t combL[3] = {0};
|
||||||
|
static int32_t combLOld[2] = {0};
|
||||||
|
int32_t combR[3] = {0};
|
||||||
|
static int32_t combROld[2] = {0};
|
||||||
|
|
||||||
|
combL[0] = valL - oldL;
|
||||||
|
combR[0] = valR - oldR;
|
||||||
|
combL[1] = combL[0] - combLOld[0];
|
||||||
|
combR[1] = combR[0] - combROld[0];
|
||||||
|
combL[2] = combL[1] - combLOld[1];
|
||||||
|
combR[2] = combR[1] - combROld[1];
|
||||||
|
// combL[2] now holds input val
|
||||||
|
// combR[2] now holds input val
|
||||||
|
oldL = valL;
|
||||||
|
oldR = valR;
|
||||||
|
combLOld[0] = combL[0];
|
||||||
|
combROld[0] = combR[0];
|
||||||
|
combLOld[1] = combL[1];
|
||||||
|
combROld[1] = combR[1];
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
int32_t integrateL[3];
|
||||||
|
int32_t integrateR[3];
|
||||||
|
static int32_t integrateLOld[3] = {0};
|
||||||
|
static int32_t integrateROld[3] = {0};
|
||||||
|
integrateL[0] = ( (j==0) ? (combL[2]) : (0) ) + integrateLOld[0];
|
||||||
|
integrateR[0] = ( (j==0) ? (combR[2]) : (0) ) + integrateROld[0];
|
||||||
|
integrateL[1] = integrateL[0] + integrateLOld[1];
|
||||||
|
integrateR[1] = integrateR[0] + integrateROld[1];
|
||||||
|
integrateL[2] = integrateL[1] + integrateLOld[2];
|
||||||
|
integrateR[2] = integrateR[1] + integrateROld[2];
|
||||||
|
// integrateL[2] now holds j'th upsampled value
|
||||||
|
// integrateR[2] now holds j'th upsampled value
|
||||||
|
*(dest+j*2) = integrateL[2] >> 4;
|
||||||
|
*(dest+j*2+1) = integrateR[2] >> 4;
|
||||||
|
integrateLOld[0] = integrateL[0];
|
||||||
|
integrateROld[0] = integrateR[0];
|
||||||
|
integrateLOld[1] = integrateL[1];
|
||||||
|
integrateROld[1] = integrateR[1];
|
||||||
|
integrateLOld[2] = integrateL[2];
|
||||||
|
integrateROld[2] = integrateR[2];
|
||||||
|
}
|
||||||
|
dest+=8;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error no interpolation method defined for oversampling.
|
||||||
|
#endif //defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
|
||||||
|
#else
|
||||||
|
memcpy_tointerleaveLR(dest, blockL->data + offsetL, blockR->data + offsetR);
|
||||||
|
offsetL += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
offsetR += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
#endif //defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
|
||||||
|
} else if (blockL) {
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
#if defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
|
||||||
|
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++) {
|
||||||
|
int32_t val = blockL->data[offsetL];
|
||||||
|
int32_t n = (oldL+val) >> 1;
|
||||||
|
*(dest+0) = (oldL+n) >> 1;
|
||||||
|
*(dest+1) = 0;
|
||||||
|
*(dest+2) = n;
|
||||||
|
*(dest+3) = 0;
|
||||||
|
*(dest+4) = (n+val) >> 1;
|
||||||
|
*(dest+5) = 0;
|
||||||
|
*(dest+6) = val;
|
||||||
|
*(dest+7) = 0;
|
||||||
|
dest+=8;
|
||||||
|
oldL = val;
|
||||||
|
}
|
||||||
|
#elif defined(AUDIO_PT8211_INTERPOLATION_CIC)
|
||||||
|
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
|
||||||
|
int32_t valL = blockL->data[offsetL];
|
||||||
|
|
||||||
|
int32_t combL[3] = {0};
|
||||||
|
static int32_t combLOld[2] = {0};
|
||||||
|
|
||||||
|
combL[0] = valL - oldL;
|
||||||
|
combL[1] = combL[0] - combLOld[0];
|
||||||
|
combL[2] = combL[1] - combLOld[1];
|
||||||
|
// combL[2] now holds input val
|
||||||
|
combLOld[0] = combL[0];
|
||||||
|
combLOld[1] = combL[1];
|
||||||
|
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
int32_t integrateL[3];
|
||||||
|
static int32_t integrateLOld[3] = {0};
|
||||||
|
integrateL[0] = ( (j==0) ? (combL[2]) : (0) ) + integrateLOld[0];
|
||||||
|
integrateL[1] = integrateL[0] + integrateLOld[1];
|
||||||
|
integrateL[2] = integrateL[1] + integrateLOld[2];
|
||||||
|
// integrateL[2] now holds j'th upsampled value
|
||||||
|
*(dest+j*2) = integrateL[2] >> 4;
|
||||||
|
integrateLOld[0] = integrateL[0];
|
||||||
|
integrateLOld[1] = integrateL[1];
|
||||||
|
integrateLOld[2] = integrateL[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill right channel with zeros:
|
||||||
|
*(dest+1) = 0;
|
||||||
|
*(dest+3) = 0;
|
||||||
|
*(dest+5) = 0;
|
||||||
|
*(dest+7) = 0;
|
||||||
|
dest+=8;
|
||||||
|
oldL = valL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error no interpolation method defined for oversampling.
|
||||||
|
#endif //defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
|
||||||
|
#else
|
||||||
|
memcpy_tointerleaveL(dest, blockL->data + offsetL);
|
||||||
|
offsetL += (AUDIO_BLOCK_SAMPLES / 2);
|
||||||
|
#endif //defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
} else if (blockR) {
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
#if defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
|
||||||
|
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetR++) {
|
||||||
|
int32_t val = blockR->data[offsetR];
|
||||||
|
int32_t n = (oldR+val) >> 1;
|
||||||
|
*(dest+0) = 0;
|
||||||
|
*(dest+1) = ((oldR+n) >> 1);
|
||||||
|
*(dest+2) = 0;
|
||||||
|
*(dest+3) = n;
|
||||||
|
*(dest+4) = 0;
|
||||||
|
*(dest+5) = ((n+val) >> 1);
|
||||||
|
*(dest+6) = 0;
|
||||||
|
*(dest+7) = val;
|
||||||
|
dest+=8;
|
||||||
|
oldR = val;
|
||||||
|
}
|
||||||
|
#elif defined(AUDIO_PT8211_INTERPOLATION_CIC)
|
||||||
|
for (int i=0; i< AUDIO_BLOCK_SAMPLES / 2; i++, offsetL++, offsetR++) {
|
||||||
|
int32_t valR = blockR->data[offsetR];
|
||||||
|
|
||||||
|
int32_t combR[3] = {0};
|
||||||
|
static int32_t combROld[2] = {0};
|
||||||
|
|
||||||
|
combR[0] = valR - oldR;
|
||||||
|
combR[1] = combR[0] - combROld[0];
|
||||||
|
combR[2] = combR[1] - combROld[1];
|
||||||
|
// combR[2] now holds input val
|
||||||
|
combROld[0] = combR[0];
|
||||||
|
combROld[1] = combR[1];
|
||||||
|
|
||||||
|
for (int j = 0; j < 4; j++) {
|
||||||
|
int32_t integrateR[3];
|
||||||
|
static int32_t integrateROld[3] = {0};
|
||||||
|
integrateR[0] = ( (j==0) ? (combR[2]) : (0) ) + integrateROld[0];
|
||||||
|
integrateR[1] = integrateR[0] + integrateROld[1];
|
||||||
|
integrateR[2] = integrateR[1] + integrateROld[2];
|
||||||
|
// integrateR[2] now holds j'th upsampled value
|
||||||
|
*(dest+j*2+1) = integrateR[2] >> 4;
|
||||||
|
integrateROld[0] = integrateR[0];
|
||||||
|
integrateROld[1] = integrateR[1];
|
||||||
|
integrateROld[2] = integrateR[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill left channel with zeros:
|
||||||
|
*(dest+0) = 0;
|
||||||
|
*(dest+2) = 0;
|
||||||
|
*(dest+4) = 0;
|
||||||
|
*(dest+6) = 0;
|
||||||
|
dest+=8;
|
||||||
|
oldR = valR;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error no interpolation method defined for oversampling.
|
||||||
|
#endif //defined(AUDIO_PT8211_INTERPOLATION_LINEAR)
|
||||||
|
#else
|
||||||
|
memcpy_tointerleaveR(dest, blockR->data + offsetR);
|
||||||
|
offsetR += AUDIO_BLOCK_SAMPLES / 2;
|
||||||
|
#endif //defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
} else {
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
memset(dest,0,AUDIO_BLOCK_SAMPLES*8);
|
||||||
|
#else
|
||||||
|
memset(dest,0,AUDIO_BLOCK_SAMPLES*2);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offsetL < AUDIO_BLOCK_SAMPLES) {
|
||||||
|
AudioOutputPT8211_2::block_left_offset = offsetL;
|
||||||
|
} else {
|
||||||
|
AudioOutputPT8211_2::block_left_offset = 0;
|
||||||
|
AudioStream::release(blockL);
|
||||||
|
AudioOutputPT8211_2::block_left_1st = AudioOutputPT8211_2::block_left_2nd;
|
||||||
|
AudioOutputPT8211_2::block_left_2nd = NULL;
|
||||||
|
}
|
||||||
|
if (offsetR < AUDIO_BLOCK_SAMPLES) {
|
||||||
|
AudioOutputPT8211_2::block_right_offset = offsetR;
|
||||||
|
} else {
|
||||||
|
AudioOutputPT8211_2::block_right_offset = 0;
|
||||||
|
AudioStream::release(blockR);
|
||||||
|
AudioOutputPT8211_2::block_right_1st = AudioOutputPT8211_2::block_right_2nd;
|
||||||
|
AudioOutputPT8211_2::block_right_2nd = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputPT8211_2::update(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
audio_block_t *block;
|
||||||
|
block = receiveReadOnly(0); // input 0 = left channel
|
||||||
|
if (block) {
|
||||||
|
__disable_irq();
|
||||||
|
if (block_left_1st == NULL) {
|
||||||
|
block_left_1st = block;
|
||||||
|
block_left_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else if (block_left_2nd == NULL) {
|
||||||
|
block_left_2nd = block;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_left_1st;
|
||||||
|
block_left_1st = block_left_2nd;
|
||||||
|
block_left_2nd = block;
|
||||||
|
block_left_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
release(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block = receiveReadOnly(1); // input 1 = right channel
|
||||||
|
if (block) {
|
||||||
|
__disable_irq();
|
||||||
|
if (block_right_1st == NULL) {
|
||||||
|
block_right_1st = block;
|
||||||
|
block_right_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else if (block_right_2nd == NULL) {
|
||||||
|
block_right_2nd = block;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_right_1st;
|
||||||
|
block_right_1st = block_right_2nd;
|
||||||
|
block_right_2nd = block;
|
||||||
|
block_right_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
release(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
|
void AudioOutputPT8211_2::config_i2s(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
|
||||||
|
//PLL:
|
||||||
|
int fs = AUDIO_SAMPLE_RATE_EXACT;
|
||||||
|
// PLL between 27*24 = 648MHz und 54*24=1296MHz
|
||||||
|
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
|
||||||
|
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
|
||||||
|
|
||||||
|
double C = ((double)fs * 256 * n1 * n2) / 24000000;
|
||||||
|
int c0 = C;
|
||||||
|
int c2 = 10000;
|
||||||
|
int c1 = C * c2 - (c0 * c2);
|
||||||
|
set_audioClock(c0, c1, c2);
|
||||||
|
|
||||||
|
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
|
||||||
|
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
|
||||||
|
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
|
||||||
|
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
|
||||||
|
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
|
||||||
|
|
||||||
|
if (I2S2_TCSR & I2S_TCSR_TE) return;
|
||||||
|
|
||||||
|
//CORE_PIN5_CONFIG = 2; //2:MCLK
|
||||||
|
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
|
||||||
|
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
|
||||||
|
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
int div = 0;
|
||||||
|
#else
|
||||||
|
int div = 3;
|
||||||
|
#endif
|
||||||
|
// configure transmitter
|
||||||
|
I2S2_TMR = 0;
|
||||||
|
I2S2_TCR1 = I2S_TCR1_RFW(0);
|
||||||
|
I2S2_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(div);
|
||||||
|
I2S2_TCR3 = I2S_TCR3_TCE;
|
||||||
|
// I2S2_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF | I2S_TCR4_FSE | I2S_TCR4_FSP | I2S_TCR4_FSD; //TDA1543
|
||||||
|
I2S2_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF /*| I2S_TCR4_FSE*/ | I2S_TCR4_FSP | I2S_TCR4_FSD; //PT8211
|
||||||
|
I2S2_TCR5 = I2S_TCR5_WNW(15) | I2S_TCR5_W0W(15) | I2S_TCR5_FBT(15);
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif
|
||||||
67
sampler/lib/Audio_SdFat/output_pt8211_2.h
Normal file
67
sampler/lib/Audio_SdFat/output_pt8211_2.h
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2016, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//Frank Bösing, Ben-Rheinland
|
||||||
|
|
||||||
|
#ifndef output_pt8211_2_h_
|
||||||
|
#define output_pt8211_2_h_
|
||||||
|
|
||||||
|
//uncomment to enable oversampling:
|
||||||
|
#define AUDIO_PT8211_OVERSAMPLING
|
||||||
|
//uncomment ONE of these to define interpolation type for oversampling:
|
||||||
|
// #define AUDIO_PT8211_INTERPOLATION_LINEAR
|
||||||
|
#define AUDIO_PT8211_INTERPOLATION_CIC
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "AudioStream.h"
|
||||||
|
#include "DMAChannel.h"
|
||||||
|
|
||||||
|
class AudioOutputPT8211_2 : public AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutputPT8211_2(void) : AudioStream(2, inputQueueArray) { begin(); }
|
||||||
|
virtual void update(void);
|
||||||
|
void begin(void);
|
||||||
|
protected:
|
||||||
|
static void config_i2s(void);
|
||||||
|
static audio_block_t *block_left_1st;
|
||||||
|
static audio_block_t *block_right_1st;
|
||||||
|
static bool update_responsibility;
|
||||||
|
static DMAChannel dma;
|
||||||
|
static void isr(void)
|
||||||
|
#if defined(AUDIO_PT8211_OVERSAMPLING)
|
||||||
|
__attribute__((optimize("unroll-loops")))
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
private:
|
||||||
|
static audio_block_t *block_left_2nd;
|
||||||
|
static audio_block_t *block_right_2nd;
|
||||||
|
static uint16_t block_left_offset;
|
||||||
|
static uint16_t block_right_offset;
|
||||||
|
audio_block_t *inputQueueArray[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -25,15 +25,7 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "output_spdif.h"
|
#include "output_spdif.h"
|
||||||
|
#include "utility/imxrt_hw.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.
|
|
||||||
|
|
||||||
audio_block_t * AudioOutputSPDIF::block_left_1st = NULL;
|
audio_block_t * AudioOutputSPDIF::block_left_1st = NULL;
|
||||||
audio_block_t * AudioOutputSPDIF::block_right_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_left_offset = 0;
|
||||||
uint16_t AudioOutputSPDIF::block_right_offset = 0;
|
uint16_t AudioOutputSPDIF::block_right_offset = 0;
|
||||||
bool AudioOutputSPDIF::update_responsibility = false;
|
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;
|
uint32_t AudioOutputSPDIF::vucp = VUCP_VALID;
|
||||||
|
|
||||||
DMAMEM static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
|
#endif
|
||||||
|
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
void AudioOutputSPDIF::begin(void)
|
void AudioOutputSPDIF::begin(void)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
@ -95,6 +64,7 @@ void AudioOutputSPDIF::begin(void)
|
||||||
|
|
||||||
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
||||||
config_SPDIF();
|
config_SPDIF();
|
||||||
|
#if defined(KINETISK)
|
||||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
||||||
|
|
||||||
const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
|
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;
|
I2S0_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
|
||||||
dma.attachInterrupt(isr);
|
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++;
|
sample = *src++;
|
||||||
|
|
||||||
//Subframe Channel 1
|
//Subframe Channel 1
|
||||||
hi = bmclookup[(uint8_t)(sample >> 8)];
|
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
|
||||||
lo = bmclookup[(uint8_t) sample];
|
lo = spdif_bmclookup[(uint8_t) sample];
|
||||||
lo ^= (~((int16_t)hi) >> 16);
|
lo ^= (~((int16_t)hi) >> 16);
|
||||||
// 16 Bit sample:
|
// 16 Bit sample:
|
||||||
*(dest+1) = ((uint32_t)lo << 16) | hi;
|
*(dest+1) = ((uint32_t)lo << 16) | hi;
|
||||||
|
|
@ -217,8 +215,8 @@ void AudioOutputSPDIF::isr(void)
|
||||||
sample = *src++;
|
sample = *src++;
|
||||||
|
|
||||||
//Subframe Channel 2
|
//Subframe Channel 2
|
||||||
hi = bmclookup[(uint8_t)(sample >> 8)];
|
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
|
||||||
lo = bmclookup[(uint8_t)sample];
|
lo = spdif_bmclookup[(uint8_t)sample];
|
||||||
lo ^= (~((int16_t)hi) >> 16);
|
lo ^= (~((int16_t)hi) >> 16);
|
||||||
|
|
||||||
*(dest+1) = ( ((uint32_t)lo << 16) | hi );
|
*(dest+1) = ( ((uint32_t)lo << 16) | hi );
|
||||||
|
|
@ -246,6 +244,11 @@ void AudioOutputSPDIF::isr(void)
|
||||||
} while (dest < end);
|
} 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)
|
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
|
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
|
||||||
// PLL is at 96 MHz in these modes
|
// PLL is at 96 MHz in these modes
|
||||||
#define MCLK_MULT 2
|
#define MCLK_MULT 2
|
||||||
|
|
@ -324,12 +327,17 @@ void AudioOutputSPDIF::update(void)
|
||||||
#define MCLK_MULT 1
|
#define MCLK_MULT 1
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
#elif F_CPU == 216000000
|
#elif F_CPU == 216000000
|
||||||
#define MCLK_MULT 8
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 153
|
#define MCLK_DIV 17
|
||||||
#define MCLK_SRC 0
|
#define MCLK_SRC 1
|
||||||
#elif F_CPU == 240000000
|
#elif F_CPU == 240000000
|
||||||
#define MCLK_MULT 4
|
#define MCLK_MULT 2
|
||||||
#define MCLK_DIV 85
|
#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
|
#elif F_CPU == 16000000
|
||||||
#define MCLK_MULT 12
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
|
|
@ -344,10 +352,12 @@ void AudioOutputSPDIF::update(void)
|
||||||
#define MCLK_SRC 0 // system clock
|
#define MCLK_SRC 0 // system clock
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
void AudioOutputSPDIF::config_SPDIF(void)
|
void AudioOutputSPDIF::config_SPDIF(void)
|
||||||
{
|
{
|
||||||
|
#if defined(KINETISK)
|
||||||
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
||||||
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
||||||
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
|
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
|
||||||
|
|
@ -371,14 +381,70 @@ void AudioOutputSPDIF::config_SPDIF(void)
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// configure pin mux for 3 clock signals (debug only)
|
// configure pin mux for 3 clock signals (debug only)
|
||||||
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
|
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
|
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
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#elif defined(KINETISL)
|
#if defined(KINETISL)
|
||||||
|
|
||||||
void AudioOutputSPDIF::update(void)
|
void AudioOutputSPDIF::update(void)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ public:
|
||||||
AudioOutputSPDIF(void) : AudioStream(2, inputQueueArray) { begin(); }
|
AudioOutputSPDIF(void) : AudioStream(2, inputQueueArray) { begin(); }
|
||||||
virtual void update(void);
|
virtual void update(void);
|
||||||
void begin(void);
|
void begin(void);
|
||||||
friend class AudioInputSPDIF;
|
//friend class AudioInputSPDIF;
|
||||||
static void mute_PCM(const bool mute);
|
static void mute_PCM(const bool mute);
|
||||||
protected:
|
protected:
|
||||||
AudioOutputSPDIF(int dummy): AudioStream(2, inputQueueArray) {}
|
//AudioOutputSPDIF(int dummy): AudioStream(2, inputQueueArray) {}
|
||||||
static void config_SPDIF(void);
|
static void config_SPDIF(void);
|
||||||
static audio_block_t *block_left_1st;
|
static audio_block_t *block_left_1st;
|
||||||
static audio_block_t *block_right_1st;
|
static audio_block_t *block_right_1st;
|
||||||
|
|
|
||||||
315
sampler/lib/Audio_SdFat/output_spdif2.cpp
Normal file
315
sampler/lib/Audio_SdFat/output_spdif2.cpp
Normal file
|
|
@ -0,0 +1,315 @@
|
||||||
|
/* SPDIF for Teensy 3.X
|
||||||
|
* Copyright (c) 2015, Frank Bösing, f.boesing@gmx.de,
|
||||||
|
* Thanks to KPC & Paul Stoffregen!
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 2015/08/23: (FB) added mute_PCM() - sets or unsets VALID in VUCP (and adjusts PARITY)
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "output_spdif2.h"
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
|
audio_block_t * AudioOutputSPDIF2::block_left_1st = NULL;
|
||||||
|
audio_block_t * AudioOutputSPDIF2::block_right_1st = NULL;
|
||||||
|
audio_block_t * AudioOutputSPDIF2::block_left_2nd = NULL;
|
||||||
|
audio_block_t * AudioOutputSPDIF2::block_right_2nd = NULL;
|
||||||
|
uint16_t AudioOutputSPDIF2::block_left_offset = 0;
|
||||||
|
uint16_t AudioOutputSPDIF2::block_right_offset = 0;
|
||||||
|
bool AudioOutputSPDIF2::update_responsibility = false;
|
||||||
|
DMAChannel AudioOutputSPDIF2::dma(false);
|
||||||
|
extern uint16_t spdif_bmclookup[256];
|
||||||
|
|
||||||
|
DMAMEM __attribute__((aligned(32)))
|
||||||
|
static uint32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4]; //2 KB
|
||||||
|
|
||||||
|
#define PREAMBLE_B (0xE8) //11101000
|
||||||
|
#define PREAMBLE_M (0xE2) //11100010
|
||||||
|
#define PREAMBLE_W (0xE4) //11100100
|
||||||
|
|
||||||
|
#define VUCP_VALID ((0xCC) << 24)
|
||||||
|
#define VUCP_INVALID ((0xD4) << 24)// To mute PCM, set VUCP = invalid.
|
||||||
|
|
||||||
|
uint32_t AudioOutputSPDIF2::vucp = VUCP_VALID;
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
|
void AudioOutputSPDIF2::begin(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
block_left_1st = NULL;
|
||||||
|
block_right_1st = NULL;
|
||||||
|
|
||||||
|
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
||||||
|
config_SPDIF();
|
||||||
|
|
||||||
|
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
|
||||||
|
const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
|
||||||
|
|
||||||
|
dma.TCD->SADDR = SPDIF_tx_buffer;
|
||||||
|
dma.TCD->SOFF = 4;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
|
||||||
|
dma.TCD->NBYTES_MLNO = nbytes_mlno;
|
||||||
|
dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
|
||||||
|
dma.TCD->DADDR = &I2S2_TDR0;
|
||||||
|
dma.TCD->DOFF = 0;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
|
||||||
|
dma.TCD->DLASTSGA = 0;
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.enable();
|
||||||
|
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE | I2S_TCSR_FR;
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
http://www.hardwarebook.info/S/PDIF
|
||||||
|
|
||||||
|
1. To make it easier and a bit faster, the parity-bit is always the same.
|
||||||
|
- With a alternating parity we had to adjust the next subframe. Instead, use a bit from the aux-info as parity.
|
||||||
|
|
||||||
|
2. The buffer is filled with an offset of 1 byte, so the last parity (which is always 0 now (see 1.) ) is written as first byte.
|
||||||
|
-> A bit easier and faster to construct both subframes.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
void AudioOutputSPDIF2::isr(void)
|
||||||
|
{
|
||||||
|
static uint16_t frame = 0;
|
||||||
|
const int16_t *src;
|
||||||
|
int32_t *end, *dest;
|
||||||
|
audio_block_t *block;
|
||||||
|
uint32_t saddr, offset;
|
||||||
|
uint16_t sample, lo, hi, aux;
|
||||||
|
|
||||||
|
saddr = (uint32_t)(dma.TCD->SADDR);
|
||||||
|
dma.clearInterrupt();
|
||||||
|
if (saddr < (uint32_t)SPDIF_tx_buffer + sizeof(SPDIF_tx_buffer) / 2) {
|
||||||
|
// DMA is transmitting the first half of the buffer
|
||||||
|
// so we must fill the second half
|
||||||
|
dest = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
|
||||||
|
end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4];
|
||||||
|
if (AudioOutputSPDIF2::update_responsibility) AudioStream::update_all();
|
||||||
|
} else {
|
||||||
|
// DMA is transmitting the second half of the buffer
|
||||||
|
// so we must fill the first half
|
||||||
|
dest = (int32_t *)SPDIF_tx_buffer;
|
||||||
|
end = (int32_t *)&SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4/2];
|
||||||
|
}
|
||||||
|
|
||||||
|
block = AudioOutputSPDIF2::block_left_1st;
|
||||||
|
if (block) {
|
||||||
|
offset = AudioOutputSPDIF2::block_left_offset;
|
||||||
|
src = &block->data[offset];
|
||||||
|
do {
|
||||||
|
|
||||||
|
sample = *src++;
|
||||||
|
|
||||||
|
//Subframe Channel 1
|
||||||
|
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
|
||||||
|
lo = spdif_bmclookup[(uint8_t) sample];
|
||||||
|
lo ^= (~((int16_t)hi) >> 16);
|
||||||
|
// 16 Bit sample:
|
||||||
|
*(dest+1) = ((uint32_t)lo << 16) | hi;
|
||||||
|
// 4 Bit Auxillary-audio-databits, the first used as parity
|
||||||
|
aux = (0xB333 ^ (((uint32_t)((int16_t)lo)) >> 17));
|
||||||
|
|
||||||
|
if (++frame > 191) {
|
||||||
|
// VUCP-Bits ("Valid, Subcode, Channelstatus, Parity) = 0 (0xcc) | Preamble (depends on Framno.) | Auxillary
|
||||||
|
*(dest+0) = vucp | (PREAMBLE_B << 16 ) | aux; //special preamble for one of 192 frames
|
||||||
|
frame = 0;
|
||||||
|
} else {
|
||||||
|
*(dest+0) = vucp | (PREAMBLE_M << 16 ) | aux;
|
||||||
|
}
|
||||||
|
dest += 4;
|
||||||
|
|
||||||
|
} while (dest < end);
|
||||||
|
offset += AUDIO_BLOCK_SAMPLES/2;
|
||||||
|
if (offset < AUDIO_BLOCK_SAMPLES) {
|
||||||
|
AudioOutputSPDIF2::block_left_offset = offset;
|
||||||
|
} else {
|
||||||
|
AudioOutputSPDIF2::block_left_offset = 0;
|
||||||
|
AudioStream::release(block);
|
||||||
|
AudioOutputSPDIF2::block_left_1st = AudioOutputSPDIF2::block_left_2nd;
|
||||||
|
AudioOutputSPDIF2::block_left_2nd = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
if ( ++frame > 191 ) {
|
||||||
|
*(dest+0) = vucp | 0x00e8cccc;
|
||||||
|
frame = 0;
|
||||||
|
} else {
|
||||||
|
*(dest+0) = vucp | 0x00e2cccc;
|
||||||
|
}
|
||||||
|
*(dest+1) = 0xccccccccUL;
|
||||||
|
|
||||||
|
dest +=4;
|
||||||
|
} while (dest < end);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dest -= AUDIO_BLOCK_SAMPLES * 4/2 - 4/2;
|
||||||
|
block = AudioOutputSPDIF2::block_right_1st;
|
||||||
|
if (block) {
|
||||||
|
offset = AudioOutputSPDIF2::block_right_offset;
|
||||||
|
src = &block->data[offset];
|
||||||
|
|
||||||
|
do {
|
||||||
|
sample = *src++;
|
||||||
|
|
||||||
|
//Subframe Channel 2
|
||||||
|
hi = spdif_bmclookup[(uint8_t)(sample >> 8)];
|
||||||
|
lo = spdif_bmclookup[(uint8_t)sample];
|
||||||
|
lo ^= (~((int16_t)hi) >> 16);
|
||||||
|
|
||||||
|
*(dest+1) = ( ((uint32_t)lo << 16) | hi );
|
||||||
|
|
||||||
|
aux = (0xB333 ^ (((uint32_t)((int16_t)lo)) >> 17));
|
||||||
|
*(dest+0) = vucp | (PREAMBLE_W << 16 ) | aux;
|
||||||
|
|
||||||
|
dest += 4;
|
||||||
|
} while (dest < end);
|
||||||
|
|
||||||
|
offset += AUDIO_BLOCK_SAMPLES/2;
|
||||||
|
if (offset < AUDIO_BLOCK_SAMPLES) {
|
||||||
|
AudioOutputSPDIF2::block_right_offset = offset;
|
||||||
|
} else {
|
||||||
|
AudioOutputSPDIF2::block_right_offset = 0;
|
||||||
|
AudioStream::release(block);
|
||||||
|
AudioOutputSPDIF2::block_right_1st = AudioOutputSPDIF2::block_right_2nd;
|
||||||
|
AudioOutputSPDIF2::block_right_2nd = NULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
do {
|
||||||
|
*dest = vucp | 0x00e4ccccUL;
|
||||||
|
*(dest+1) = 0xccccccccUL;
|
||||||
|
dest += 4 ;
|
||||||
|
} while (dest < end);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IMXRT_CACHE_ENABLED >= 2
|
||||||
|
dest -= AUDIO_BLOCK_SAMPLES * 4/2 + 4/2;
|
||||||
|
arm_dcache_flush_delete(dest, sizeof(SPDIF_tx_buffer) / 2 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputSPDIF2::mute_PCM(const bool mute)
|
||||||
|
{
|
||||||
|
vucp = mute?VUCP_INVALID:VUCP_VALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputSPDIF2::update(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
audio_block_t *block;
|
||||||
|
block = receiveReadOnly(0); // input 0 = left channel
|
||||||
|
if (block) {
|
||||||
|
|
||||||
|
__disable_irq();
|
||||||
|
if (block_left_1st == NULL) {
|
||||||
|
block_left_1st = block;
|
||||||
|
block_left_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else if (block_left_2nd == NULL) {
|
||||||
|
block_left_2nd = block;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_left_1st;
|
||||||
|
block_left_1st = block_left_2nd;
|
||||||
|
block_left_2nd = block;
|
||||||
|
block_left_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
release(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
block = receiveReadOnly(1); // input 1 = right channel
|
||||||
|
if (block) {
|
||||||
|
__disable_irq();
|
||||||
|
if (block_right_1st == NULL) {
|
||||||
|
block_right_1st = block;
|
||||||
|
block_right_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
} else if (block_right_2nd == NULL) {
|
||||||
|
block_right_2nd = block;
|
||||||
|
__enable_irq();
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_right_1st;
|
||||||
|
block_right_1st = block_right_2nd;
|
||||||
|
block_right_2nd = block;
|
||||||
|
block_right_offset = 0;
|
||||||
|
__enable_irq();
|
||||||
|
release(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
|
void AudioOutputSPDIF2::config_SPDIF(void)
|
||||||
|
{
|
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
|
||||||
|
//PLL:
|
||||||
|
int fs = AUDIO_SAMPLE_RATE_EXACT;
|
||||||
|
// PLL between 27*24 = 648MHz und 54*24=1296MHz
|
||||||
|
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
|
||||||
|
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
|
||||||
|
|
||||||
|
double C = ((double)fs * 256 * n1 * n2) / 24000000;
|
||||||
|
int c0 = C;
|
||||||
|
int c2 = 10000;
|
||||||
|
int c1 = C * c2 - (c0 * c2);
|
||||||
|
set_audioClock(c0, c1, c2);
|
||||||
|
|
||||||
|
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
|
||||||
|
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4,
|
||||||
|
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1)
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1);
|
||||||
|
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
|
||||||
|
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
|
||||||
|
|
||||||
|
// configure transmitter
|
||||||
|
I2S2_TMR = 0;
|
||||||
|
I2S2_TCR1 = I2S_TCR1_RFW(0); // watermark
|
||||||
|
I2S2_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(0);
|
||||||
|
I2S2_TCR3 = I2S_TCR3_TCE;
|
||||||
|
|
||||||
|
//4 Words per Frame 32 Bit Word-Length -> 128 Bit Frame-Length, MSB First:
|
||||||
|
I2S2_TCR4 = I2S_TCR4_FRSZ(3) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF | I2S_TCR4_FSP | I2S_TCR4_FSD;
|
||||||
|
I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
|
||||||
|
#if 0
|
||||||
|
//debug only:
|
||||||
|
CORE_PIN5_CONFIG = 2; //2:MCLK 11.43MHz
|
||||||
|
CORE_PIN4_CONFIG = 2; //2:TX_BCLK 5 MHz
|
||||||
|
CORE_PIN3_CONFIG = 2; //2:TX_SYNC 44.1 KHz
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
57
sampler/lib/Audio_SdFat/output_spdif2.h
Normal file
57
sampler/lib/Audio_SdFat/output_spdif2.h
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* SPDIF for Teensy 3.X
|
||||||
|
* Copyright (c) 2015, Frank Bösing, f.boesing@gmx.de,
|
||||||
|
* Thanks to KPC & Paul Stoffregen!
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef output_SPDIF2_h_
|
||||||
|
#define output_SPDIF2_h_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "AudioStream.h"
|
||||||
|
#include "DMAChannel.h"
|
||||||
|
|
||||||
|
class AudioOutputSPDIF2 : public AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutputSPDIF2(void) : AudioStream(2, inputQueueArray) { begin(); }
|
||||||
|
virtual void update(void);
|
||||||
|
void begin(void);
|
||||||
|
//friend class AudioInputSPDIF;
|
||||||
|
static void mute_PCM(const bool mute);
|
||||||
|
protected:
|
||||||
|
//AudioOutputSPDIF2(int dummy): AudioStream(2, inputQueueArray) {}
|
||||||
|
static void config_SPDIF(void);
|
||||||
|
static audio_block_t *block_left_1st;
|
||||||
|
static audio_block_t *block_right_1st;
|
||||||
|
static bool update_responsibility;
|
||||||
|
static DMAChannel dma;
|
||||||
|
static void isr(void);
|
||||||
|
private:
|
||||||
|
static uint32_t vucp;
|
||||||
|
static audio_block_t *block_left_2nd;
|
||||||
|
static audio_block_t *block_right_2nd;
|
||||||
|
static uint16_t block_left_offset;
|
||||||
|
static uint16_t block_right_offset;
|
||||||
|
audio_block_t *inputQueueArray[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
258
sampler/lib/Audio_SdFat/output_spdif3.cpp
Normal file
258
sampler/lib/Audio_SdFat/output_spdif3.cpp
Normal file
|
|
@ -0,0 +1,258 @@
|
||||||
|
/* Hardware-SPDIF for Teensy 4
|
||||||
|
* Copyright (c) 2019, Frank Bösing, f.boesing@gmx.de
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
http://www.hardwarebook.info/S/PDIF
|
||||||
|
https://www.mikrocontroller.net/articles/S/PDIF
|
||||||
|
https://en.wikipedia.org/wiki/S/PDIF
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "output_spdif3.h"
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
#include "memcpy_audio.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
audio_block_t * AudioOutputSPDIF3::block_left_1st = nullptr;
|
||||||
|
audio_block_t * AudioOutputSPDIF3::block_right_1st = nullptr;
|
||||||
|
audio_block_t * AudioOutputSPDIF3::block_left_2nd = nullptr;
|
||||||
|
audio_block_t * AudioOutputSPDIF3::block_right_2nd = nullptr;
|
||||||
|
bool AudioOutputSPDIF3::update_responsibility = false;
|
||||||
|
DMAChannel AudioOutputSPDIF3::dma(false);
|
||||||
|
|
||||||
|
DMAMEM __attribute__((aligned(32)))
|
||||||
|
static int32_t SPDIF_tx_buffer[AUDIO_BLOCK_SAMPLES * 4];
|
||||||
|
DMAMEM __attribute__((aligned(32)))
|
||||||
|
audio_block_t AudioOutputSPDIF3::block_silent;
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
|
void AudioOutputSPDIF3::begin(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
block_left_1st = nullptr;
|
||||||
|
block_right_1st = nullptr;
|
||||||
|
memset(&block_silent, 0, sizeof(block_silent));
|
||||||
|
uint32_t fs = AUDIO_SAMPLE_RATE_EXACT;
|
||||||
|
// PLL between 27*24 = 648MHz und 54*24=1296MHz
|
||||||
|
// n1, n2 choosen for compatibility with I2S (same PLL frequency) :
|
||||||
|
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
|
||||||
|
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
|
||||||
|
double C = ((double)fs * 256 * n1 * n2) / 24000000;
|
||||||
|
int c0 = C;
|
||||||
|
int c2 = 10000;
|
||||||
|
int c1 = C * c2 - (c0 * c2);
|
||||||
|
set_audioClock(c0, c1, c2);
|
||||||
|
|
||||||
|
double pllclock = (c0 + (double)c1 / c2) * 24000000; //677376000 Hz
|
||||||
|
|
||||||
|
//use new pred/podf values
|
||||||
|
n1 = 7; //0: divide by 1 (do not use with high input frequencies), 1:/2, 2: /3, 7:/8
|
||||||
|
n2 = 0; //0: divide by 1, 7: divide by 8
|
||||||
|
|
||||||
|
uint32_t clock = pllclock / (1 + n1) / (1 + n2);
|
||||||
|
uint32_t clkdiv = clock / (fs * 64); // 1 .. 128
|
||||||
|
uint32_t mod = clock % (fs * 64);
|
||||||
|
if (mod > ((fs * 64) / 2)) clkdiv += 1; //nearest divider
|
||||||
|
|
||||||
|
CCM_CCGR5 &= ~CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate off
|
||||||
|
|
||||||
|
CCM_CDCDR = (CCM_CDCDR & ~(CCM_CDCDR_SPDIF0_CLK_SEL_MASK | CCM_CDCDR_SPDIF0_CLK_PRED_MASK | CCM_CDCDR_SPDIF0_CLK_PODF_MASK))
|
||||||
|
| CCM_CDCDR_SPDIF0_CLK_SEL(0) // 0 PLL4, 1 PLL3 PFD2, 2 PLL5, 3 pll3_sw_clk
|
||||||
|
| CCM_CDCDR_SPDIF0_CLK_PRED(n1)
|
||||||
|
| CCM_CDCDR_SPDIF0_CLK_PODF(n2);
|
||||||
|
|
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SPDIF(CCM_CCGR_ON); //Clock gate on
|
||||||
|
|
||||||
|
if ((SPDIF_SCR & 0x400) != 0) { //If default value:
|
||||||
|
SPDIF_SCR = SPDIF_SCR_SOFT_RESET; //Reset SPDIF
|
||||||
|
while (SPDIF_SCR & SPDIF_SCR_SOFT_RESET) {;} //Wait for Reset (takes 8 cycles)
|
||||||
|
SPDIF_SCR = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDIF_SCR |=
|
||||||
|
SPDIF_SCR_RXFIFOFULL_SEL(0) |// Full interrupt if at least 1 sample in Rx left and right FIFOs
|
||||||
|
SPDIF_SCR_RXAUTOSYNC |
|
||||||
|
SPDIF_SCR_TXAUTOSYNC |
|
||||||
|
SPDIF_SCR_TXFIFOEMPTY_SEL(3) | // Empty interrupt if at most 4 sample in Tx left and right FIFOs
|
||||||
|
SPDIF_SCR_TXFIFO_CTRL(1) | // 0:Send zeros 1: normal operation
|
||||||
|
SPDIF_SCR_DMA_TX_EN |
|
||||||
|
//SPDIF_SCR_VALCTRL | // Outgoing Validity always clear
|
||||||
|
SPDIF_SCR_TXSEL(5) | //0:off and output 0, 1:Feed-though SPDIFIN, 5:Tx Normal operation
|
||||||
|
SPDIF_SCR_USRC_SEL(3); // No embedded U channel
|
||||||
|
SPDIF_SRCD = 0;
|
||||||
|
SPDIF_STCSCH = 0;
|
||||||
|
SPDIF_STCSCL = 0;
|
||||||
|
|
||||||
|
SPDIF_SRCD = 0;
|
||||||
|
|
||||||
|
SPDIF_STC = SPDIF_STC_TXCLK_SOURCE(1) //tx_clk input (from SPDIF0_CLK_ROOT)
|
||||||
|
| SPDIF_STC_SYSCLK_DF(0)
|
||||||
|
| SPDIF_STC_TXCLK_DF(clkdiv - 1);
|
||||||
|
|
||||||
|
const int nbytes_mlno = 2 * 4; // 8 Bytes per minor loop
|
||||||
|
|
||||||
|
dma.TCD->SADDR = SPDIF_tx_buffer;
|
||||||
|
dma.TCD->SOFF = 4;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
|
||||||
|
dma.TCD->NBYTES_MLNO = DMA_TCD_NBYTES_MLOFFYES_NBYTES(nbytes_mlno) | DMA_TCD_NBYTES_DMLOE |
|
||||||
|
DMA_TCD_NBYTES_MLOFFYES_MLOFF(-8);
|
||||||
|
dma.TCD->SLAST = -sizeof(SPDIF_tx_buffer);
|
||||||
|
dma.TCD->DADDR = &SPDIF_STL;
|
||||||
|
dma.TCD->DOFF = 4;
|
||||||
|
dma.TCD->DLASTSGA = -8;
|
||||||
|
//dma.TCD->ATTR_DST = ((31 - __builtin_clz(8)) << 3);
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(SPDIF_tx_buffer) / nbytes_mlno;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SPDIF_TX);
|
||||||
|
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.enable();
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
|
||||||
|
CORE_PIN14_CONFIG = 3; //3:SPDIF_OUT
|
||||||
|
SPDIF_STC |= SPDIF_STC_TX_ALL_CLK_EN;
|
||||||
|
// pinMode(13, OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputSPDIF3::isr(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
const int16_t *src_left, *src_right;
|
||||||
|
const int32_t *end;
|
||||||
|
int32_t *dest;
|
||||||
|
audio_block_t *block_left, *block_right;
|
||||||
|
uint32_t saddr;
|
||||||
|
|
||||||
|
saddr = (uint32_t)(dma.TCD->SADDR);
|
||||||
|
dma.clearInterrupt();
|
||||||
|
if (saddr < (uint32_t)SPDIF_tx_buffer + sizeof(SPDIF_tx_buffer) / 2) {
|
||||||
|
// DMA is transmitting the first half of the buffer
|
||||||
|
// so we must fill the second half
|
||||||
|
dest = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*2;
|
||||||
|
end = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*4;
|
||||||
|
} else {
|
||||||
|
// DMA is transmitting the second half of the buffer
|
||||||
|
// so we must fill the first half
|
||||||
|
dest = SPDIF_tx_buffer;
|
||||||
|
end = SPDIF_tx_buffer + AUDIO_BLOCK_SAMPLES*2;
|
||||||
|
}
|
||||||
|
block_left = block_left_1st;
|
||||||
|
if (!block_left) block_left = &block_silent;
|
||||||
|
block_right = block_right_1st;
|
||||||
|
if (!block_right) block_right = &block_silent;
|
||||||
|
|
||||||
|
src_left = (const int16_t *)(block_left->data);
|
||||||
|
src_right = (const int16_t *)(block_right->data);
|
||||||
|
|
||||||
|
do {
|
||||||
|
|
||||||
|
*dest++ = (*src_left++) << 8;
|
||||||
|
*dest++ = (*src_right++) << 8;
|
||||||
|
|
||||||
|
*dest++ = (*src_left++) << 8;
|
||||||
|
*dest++ = (*src_right++) << 8;
|
||||||
|
|
||||||
|
*dest++ = (*src_left++) << 8;
|
||||||
|
*dest++ = (*src_right++) << 8;
|
||||||
|
|
||||||
|
*dest++ = (*src_left++) << 8;
|
||||||
|
*dest++ = (*src_right++) << 8;
|
||||||
|
|
||||||
|
#if IMXRT_CACHE_ENABLED >= 2
|
||||||
|
SCB_CACHE_DCCIMVAC = (uint32_t) dest - 32;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} while (dest < end);
|
||||||
|
|
||||||
|
if (block_left != &block_silent) {
|
||||||
|
release(block_left);
|
||||||
|
block_left_1st = block_left_2nd;
|
||||||
|
block_left_2nd = nullptr;
|
||||||
|
}
|
||||||
|
if (block_right != &block_silent) {
|
||||||
|
release(block_right);
|
||||||
|
block_right_1st = block_right_2nd;
|
||||||
|
block_right_2nd = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update_responsibility) update_all();
|
||||||
|
//digitalWriteFast(13,!digitalReadFast(13));
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputSPDIF3::update(void)
|
||||||
|
{
|
||||||
|
audio_block_t *block_left, *block_right;
|
||||||
|
|
||||||
|
block_left = receiveReadOnly(0); // input 0
|
||||||
|
block_right = receiveReadOnly(1); // input 1
|
||||||
|
__disable_irq();
|
||||||
|
if (block_left) {
|
||||||
|
if (block_left_1st == nullptr) {
|
||||||
|
block_left_1st = block_left;
|
||||||
|
block_left = nullptr;
|
||||||
|
} else if (block_left_2nd == nullptr) {
|
||||||
|
block_left_2nd = block_left;
|
||||||
|
block_left = nullptr;
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_left_1st;
|
||||||
|
block_left_1st = block_left_2nd;
|
||||||
|
block_left_2nd = block_left;
|
||||||
|
block_left = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (block_right) {
|
||||||
|
if (block_right_1st == nullptr) {
|
||||||
|
block_right_1st = block_right;
|
||||||
|
block_right = nullptr;
|
||||||
|
} else if (block_right_2nd == nullptr) {
|
||||||
|
block_right_2nd = block_right;
|
||||||
|
block_right = nullptr;
|
||||||
|
} else {
|
||||||
|
audio_block_t *tmp = block_right_1st;
|
||||||
|
block_right_1st = block_right_2nd;
|
||||||
|
block_right_2nd = block_right;
|
||||||
|
block_right = tmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__enable_irq();
|
||||||
|
if (block_left) {
|
||||||
|
release(block_left);
|
||||||
|
}
|
||||||
|
if (block_right) {
|
||||||
|
release(block_right);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputSPDIF3::mute_PCM(const bool mute)
|
||||||
|
{
|
||||||
|
if (mute)
|
||||||
|
SPDIF_SCR |= SPDIF_SCR_VALCTRL;
|
||||||
|
else
|
||||||
|
SPDIF_SCR &= ~SPDIF_SCR_VALCTRL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
53
sampler/lib/Audio_SdFat/output_spdif3.h
Normal file
53
sampler/lib/Audio_SdFat/output_spdif3.h
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
/* Hardware-SPDIF for Teensy 4
|
||||||
|
* Copyright (c) 2019, Frank Bösing, f.boesing@gmx.de
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef output_SPDIF3_h_
|
||||||
|
#define output_SPDIF3_h_
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <AudioStream.h>
|
||||||
|
#include <DMAChannel.h>
|
||||||
|
|
||||||
|
class AudioOutputSPDIF3 : public AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutputSPDIF3(void) : AudioStream(2, inputQueueArray) { begin(); }
|
||||||
|
virtual void update(void);
|
||||||
|
void begin(void);
|
||||||
|
//friend class AudioInputSPDIF;
|
||||||
|
static void mute_PCM(const bool mute);
|
||||||
|
protected:
|
||||||
|
//AudioOutputSPDIF3(int dummy): AudioStream(2, inputQueueArray) {}
|
||||||
|
static audio_block_t *block_left_1st;
|
||||||
|
static audio_block_t *block_right_1st;
|
||||||
|
static bool update_responsibility;
|
||||||
|
static DMAChannel dma;
|
||||||
|
static void isr(void);
|
||||||
|
private:
|
||||||
|
static audio_block_t *block_left_2nd;
|
||||||
|
static audio_block_t *block_right_2nd;
|
||||||
|
static audio_block_t block_silent;
|
||||||
|
audio_block_t *inputQueueArray[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -27,27 +27,31 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "output_tdm.h"
|
#include "output_tdm.h"
|
||||||
#include "memcpy_audio.h"
|
#include "memcpy_audio.h"
|
||||||
#if defined(KINETISK)
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
audio_block_t * AudioOutputTDM::block_input[16] = {
|
audio_block_t * AudioOutputTDM::block_input[16] = {
|
||||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||||
};
|
};
|
||||||
bool AudioOutputTDM::update_responsibility = false;
|
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);
|
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)
|
void AudioOutputTDM::begin(void)
|
||||||
{
|
{
|
||||||
dma.begin(true); // Allocate the DMA channel first
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
for (int i=0; i < 16; i++) {
|
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?
|
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
||||||
config_tdm();
|
config_tdm();
|
||||||
|
#if defined(KINETISK)
|
||||||
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0
|
||||||
|
|
||||||
dma.TCD->SADDR = tdm_tx_buffer;
|
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->BITER_ELINKNO = sizeof(tdm_tx_buffer) / 4;
|
||||||
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_I2S0_TX);
|
||||||
|
|
||||||
update_responsibility = update_setup();
|
update_responsibility = update_setup();
|
||||||
dma.enable();
|
dma.enable();
|
||||||
|
|
||||||
I2S0_TCSR = I2S_TCSR_SR;
|
I2S0_TCSR = I2S_TCSR_SR;
|
||||||
I2S0_TCSR = I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
|
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);
|
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;
|
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++;
|
in1 = *src1++;
|
||||||
in2 = *src2++;
|
in2 = *src2++;
|
||||||
out1 = (in1 << 16) | (in2 & 0xFFFF);
|
out1 = (in1 << 16) | (in2 & 0xFFFF);
|
||||||
out2 = (in1 & 0xFFFF0000) | (in2 >> 16);
|
out2 = (in1 & 0xFFFF0000) | (in2 >> 16);
|
||||||
*dest = out1;
|
*dest = out1;
|
||||||
*(dest + 8) = out2;
|
*(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;
|
dest = tdm_tx_buffer;
|
||||||
}
|
}
|
||||||
if (update_responsibility) AudioStream::update_all();
|
if (update_responsibility) AudioStream::update_all();
|
||||||
|
|
||||||
|
#if IMXRT_CACHE_ENABLED >= 2
|
||||||
|
uint32_t *dc = dest;
|
||||||
|
#endif
|
||||||
|
|
||||||
for (i=0; i < 16; i += 2) {
|
for (i=0; i < 16; i += 2) {
|
||||||
src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros;
|
src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros;
|
||||||
src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros;
|
src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros;
|
||||||
memcpy_tdm_tx(dest, src1, src2);
|
memcpy_tdm_tx(dest, src1, src2);
|
||||||
dest++;
|
dest++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IMXRT_CACHE_ENABLED >= 2
|
||||||
|
arm_dcache_flush_delete(dc, sizeof(tdm_tx_buffer) / 2 );
|
||||||
|
#endif
|
||||||
|
|
||||||
for (i=0; i < 16; i++) {
|
for (i=0; i < 16; i++) {
|
||||||
if (block_input[i]) {
|
if (block_input[i]) {
|
||||||
release(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
|
// 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
|
#if F_CPU == 96000000 || F_CPU == 48000000 || F_CPU == 24000000
|
||||||
|
|
@ -162,12 +209,17 @@ void AudioOutputTDM::update(void)
|
||||||
#define MCLK_MULT 2
|
#define MCLK_MULT 2
|
||||||
#define MCLK_DIV 17
|
#define MCLK_DIV 17
|
||||||
#elif F_CPU == 216000000
|
#elif F_CPU == 216000000
|
||||||
#define MCLK_MULT 16
|
#define MCLK_MULT 12
|
||||||
#define MCLK_DIV 153
|
#define MCLK_DIV 17
|
||||||
#define MCLK_SRC 0
|
#define MCLK_SRC 1
|
||||||
#elif F_CPU == 240000000
|
#elif F_CPU == 240000000
|
||||||
#define MCLK_MULT 8
|
#define MCLK_MULT 2
|
||||||
#define MCLK_DIV 85
|
#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
|
#else
|
||||||
#error "This CPU Clock Speed is not supported by the Audio library";
|
#error "This CPU Clock Speed is not supported by the Audio library";
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -179,9 +231,11 @@ void AudioOutputTDM::update(void)
|
||||||
#define MCLK_SRC 0 // system clock
|
#define MCLK_SRC 0 // system clock
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
void AudioOutputTDM::config_tdm(void)
|
void AudioOutputTDM::config_tdm(void)
|
||||||
{
|
{
|
||||||
|
#if defined(KINETISK)
|
||||||
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
SIM_SCGC6 |= SIM_SCGC6_I2S;
|
||||||
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
SIM_SCGC7 |= SIM_SCGC7_DMA;
|
||||||
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
|
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);
|
I2S0_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
|
||||||
|
|
||||||
// configure pin mux for 3 clock signals
|
// configure pin mux for 3 clock signals
|
||||||
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
|
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
|
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
|
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
|
|
||||||
|
|
|
||||||
220
sampler/lib/Audio_SdFat/output_tdm2.cpp
Normal file
220
sampler/lib/Audio_SdFat/output_tdm2.cpp
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1062__)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include "output_tdm2.h"
|
||||||
|
#include "memcpy_audio.h"
|
||||||
|
#include "utility/imxrt_hw.h"
|
||||||
|
|
||||||
|
audio_block_t * AudioOutputTDM2::block_input[16] = {
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr
|
||||||
|
};
|
||||||
|
bool AudioOutputTDM2::update_responsibility = false;
|
||||||
|
DMAChannel AudioOutputTDM2::dma(false);
|
||||||
|
DMAMEM __attribute__((aligned(32)))
|
||||||
|
static uint32_t zeros[AUDIO_BLOCK_SAMPLES/2];
|
||||||
|
DMAMEM __attribute__((aligned(32)))
|
||||||
|
static uint32_t tdm_tx_buffer[AUDIO_BLOCK_SAMPLES*16];
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputTDM2::begin(void)
|
||||||
|
{
|
||||||
|
dma.begin(true); // Allocate the DMA channel first
|
||||||
|
|
||||||
|
for (int i=0; i < 16; i++) {
|
||||||
|
block_input[i] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should we set & clear the I2S_TCSR_SR bit here?
|
||||||
|
config_tdm();
|
||||||
|
|
||||||
|
CORE_PIN2_CONFIG = 2; //2:TX_DATA0
|
||||||
|
|
||||||
|
dma.TCD->SADDR = tdm_tx_buffer;
|
||||||
|
dma.TCD->SOFF = 4;
|
||||||
|
dma.TCD->ATTR = DMA_TCD_ATTR_SSIZE(2) | DMA_TCD_ATTR_DSIZE(2);
|
||||||
|
dma.TCD->NBYTES_MLNO = 4;
|
||||||
|
dma.TCD->SLAST = -sizeof(tdm_tx_buffer);
|
||||||
|
dma.TCD->DADDR = &I2S2_TDR0;
|
||||||
|
dma.TCD->DOFF = 0;
|
||||||
|
dma.TCD->CITER_ELINKNO = sizeof(tdm_tx_buffer) / 4;
|
||||||
|
dma.TCD->DLASTSGA = 0;
|
||||||
|
dma.TCD->BITER_ELINKNO = sizeof(tdm_tx_buffer) / 4;
|
||||||
|
dma.TCD->CSR = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
|
||||||
|
dma.triggerAtHardwareEvent(DMAMUX_SOURCE_SAI2_TX);
|
||||||
|
|
||||||
|
update_responsibility = update_setup();
|
||||||
|
dma.enable();
|
||||||
|
|
||||||
|
//I2S2_RCSR |= I2S_RCSR_RE;
|
||||||
|
I2S2_TCSR |= I2S_TCSR_TE | I2S_TCSR_BCE | I2S_TCSR_FRDE;
|
||||||
|
|
||||||
|
dma.attachInterrupt(isr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: needs optimization...
|
||||||
|
static void memcpy_tdm_tx(uint32_t *dest, const uint32_t *src1, const uint32_t *src2)
|
||||||
|
{
|
||||||
|
uint32_t i, in1, in2, out1, out2;
|
||||||
|
|
||||||
|
for (i=0; i < AUDIO_BLOCK_SAMPLES/2; i++) {
|
||||||
|
|
||||||
|
in1 = *src1++;
|
||||||
|
in2 = *src2++;
|
||||||
|
out1 = (in1 << 16) | (in2 & 0xFFFF);
|
||||||
|
out2 = (in1 & 0xFFFF0000) | (in2 >> 16);
|
||||||
|
*dest = out1;
|
||||||
|
*(dest + 8) = out2;
|
||||||
|
|
||||||
|
in1 = *src1++;
|
||||||
|
in2 = *src2++;
|
||||||
|
out1 = (in1 << 16) | (in2 & 0xFFFF);
|
||||||
|
out2 = (in1 & 0xFFFF0000) | (in2 >> 16);
|
||||||
|
*(dest + 16)= out1;
|
||||||
|
*(dest + 24) = out2;
|
||||||
|
|
||||||
|
dest += 32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputTDM2::isr(void)
|
||||||
|
{
|
||||||
|
uint32_t *dest, *dc;
|
||||||
|
const uint32_t *src1, *src2;
|
||||||
|
uint32_t i, saddr;
|
||||||
|
|
||||||
|
saddr = (uint32_t)(dma.TCD->SADDR);
|
||||||
|
dma.clearInterrupt();
|
||||||
|
if (saddr < (uint32_t)tdm_tx_buffer + sizeof(tdm_tx_buffer) / 2) {
|
||||||
|
// DMA is transmitting the first half of the buffer
|
||||||
|
// so we must fill the second half
|
||||||
|
dest = tdm_tx_buffer + AUDIO_BLOCK_SAMPLES*8;
|
||||||
|
} else {
|
||||||
|
// DMA is transmitting the second half of the buffer
|
||||||
|
// so we must fill the first half
|
||||||
|
dest = tdm_tx_buffer;
|
||||||
|
}
|
||||||
|
if (update_responsibility) AudioStream::update_all();
|
||||||
|
dc = dest;
|
||||||
|
for (i=0; i < 16; i += 2) {
|
||||||
|
src1 = block_input[i] ? (uint32_t *)(block_input[i]->data) : zeros;
|
||||||
|
src2 = block_input[i+1] ? (uint32_t *)(block_input[i+1]->data) : zeros;
|
||||||
|
memcpy_tdm_tx(dest, src1, src2);
|
||||||
|
dest++;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if IMXRT_CACHE_ENABLED >= 2
|
||||||
|
arm_dcache_flush_delete(dc, sizeof(tdm_tx_buffer) / 2 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i=0; i < 16; i++) {
|
||||||
|
if (block_input[i]) {
|
||||||
|
release(block_input[i]);
|
||||||
|
block_input[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AudioOutputTDM2::update(void)
|
||||||
|
{
|
||||||
|
audio_block_t *prev[16];
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
__disable_irq();
|
||||||
|
for (i=0; i < 16; i++) {
|
||||||
|
prev[i] = block_input[i];
|
||||||
|
block_input[i] = receiveReadOnly(i);
|
||||||
|
}
|
||||||
|
__enable_irq();
|
||||||
|
for (i=0; i < 16; i++) {
|
||||||
|
if (prev[i]) release(prev[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioOutputTDM2::config_tdm(void)
|
||||||
|
{
|
||||||
|
CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);
|
||||||
|
|
||||||
|
// if either transmitter or receiver is enabled, do nothing
|
||||||
|
if (I2S2_TCSR & I2S_TCSR_TE) return;
|
||||||
|
if (I2S2_RCSR & I2S_RCSR_RE) return;
|
||||||
|
//PLL:
|
||||||
|
int fs = AUDIO_SAMPLE_RATE_EXACT; //176.4 khZ
|
||||||
|
// PLL between 27*24 = 648MHz und 54*24=1296MHz
|
||||||
|
int n1 = 4; //SAI prescaler 4 => (n1*n2) = multiple of 4
|
||||||
|
int n2 = 1 + (24000000 * 27) / (fs * 256 * n1);
|
||||||
|
|
||||||
|
double C = ((double)fs * 256 * n1 * n2) / 24000000;
|
||||||
|
int c0 = C;
|
||||||
|
int c2 = 10000;
|
||||||
|
int c1 = C * c2 - (c0 * c2);
|
||||||
|
set_audioClock(c0, c1, c2);
|
||||||
|
// clear SAI1_CLK register locations
|
||||||
|
|
||||||
|
CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
|
||||||
|
| CCM_CSCMR1_SAI2_CLK_SEL(2); // &0x03 // (0,1,2): PLL3PFD0, PLL5, PLL4
|
||||||
|
|
||||||
|
n1 = n1 / 2; //Double Speed for TDM
|
||||||
|
|
||||||
|
CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PRED(n1-1) // &0x07
|
||||||
|
| CCM_CS2CDR_SAI2_CLK_PODF(n2-1); // &0x3f
|
||||||
|
|
||||||
|
IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
|
||||||
|
| (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0)); //Select MCLK
|
||||||
|
|
||||||
|
// configure transmitter
|
||||||
|
int rsync = 1;
|
||||||
|
int tsync = 0;
|
||||||
|
|
||||||
|
I2S2_TMR = 0;
|
||||||
|
I2S2_TCR1 = I2S_TCR1_RFW(4);
|
||||||
|
I2S2_TCR2 = I2S_TCR2_SYNC(tsync) | I2S_TCR2_BCP | I2S_TCR2_MSEL(1)
|
||||||
|
| I2S_TCR2_BCD | I2S_TCR2_DIV(0);
|
||||||
|
I2S2_TCR3 = I2S_TCR3_TCE;
|
||||||
|
I2S2_TCR4 = I2S_TCR4_FRSZ(7) | I2S_TCR4_SYWD(0) | I2S_TCR4_MF
|
||||||
|
| I2S_TCR4_FSE | I2S_TCR4_FSD;
|
||||||
|
I2S2_TCR5 = I2S_TCR5_WNW(31) | I2S_TCR5_W0W(31) | I2S_TCR5_FBT(31);
|
||||||
|
|
||||||
|
// configure receiver (sync'd to transmitter clocks)
|
||||||
|
I2S2_RMR = 0;
|
||||||
|
I2S2_RCR1 = I2S_RCR1_RFW(4);
|
||||||
|
I2S2_RCR2 = I2S_RCR2_SYNC(rsync) | I2S_TCR2_BCP | I2S_RCR2_MSEL(1)
|
||||||
|
| I2S_RCR2_BCD | I2S_RCR2_DIV(0);
|
||||||
|
I2S2_RCR3 = I2S_RCR3_RCE;
|
||||||
|
I2S2_RCR4 = I2S_RCR4_FRSZ(7) | I2S_RCR4_SYWD(0) | I2S_RCR4_MF
|
||||||
|
| I2S_RCR4_FSE | I2S_RCR4_FSD;
|
||||||
|
I2S2_RCR5 = I2S_RCR5_WNW(31) | I2S_RCR5_W0W(31) | I2S_RCR5_FBT(31);
|
||||||
|
|
||||||
|
CORE_PIN33_CONFIG = 2; //2:MCLK
|
||||||
|
CORE_PIN4_CONFIG = 2; //2:TX_BCLK
|
||||||
|
CORE_PIN3_CONFIG = 2; //2:TX_SYNC
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
54
sampler/lib/Audio_SdFat/output_tdm2.h
Normal file
54
sampler/lib/Audio_SdFat/output_tdm2.h
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2017, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
#ifndef output_tdm2_h_
|
||||||
|
#define output_tdm2_h_
|
||||||
|
|
||||||
|
#include "Arduino.h"
|
||||||
|
#include "AudioStream.h"
|
||||||
|
#include "DMAChannel.h"
|
||||||
|
|
||||||
|
class AudioOutputTDM2 : public AudioStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AudioOutputTDM2(void) : AudioStream(16, inputQueueArray) { begin(); }
|
||||||
|
virtual void update(void);
|
||||||
|
void begin(void);
|
||||||
|
friend class AudioInputTDM2;
|
||||||
|
protected:
|
||||||
|
static void config_tdm(void);
|
||||||
|
static audio_block_t *block_input[16];
|
||||||
|
static bool update_responsibility;
|
||||||
|
static DMAChannel dma;
|
||||||
|
static void isr(void);
|
||||||
|
private:
|
||||||
|
audio_block_t *inputQueueArray[16];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
@ -34,7 +34,7 @@
|
||||||
class AudioPlaySdWav : public AudioStream
|
class AudioPlaySdWav : public AudioStream
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AudioPlaySdWav(void) : AudioStream(0, NULL) { begin(); }
|
AudioPlaySdWav(void) : AudioStream(0, NULL), block_left(NULL), block_right(NULL) { begin(); }
|
||||||
void begin(void);
|
void begin(void);
|
||||||
bool play(const char *filename);
|
bool play(const char *filename);
|
||||||
void stop(void);
|
void stop(void);
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@
|
||||||
// compute (a - b) / c
|
// compute (a - b) / c
|
||||||
// handling 32 bit interger overflow at every step
|
// handling 32 bit interger overflow at every step
|
||||||
// without resorting to slow 64 bit math
|
// 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) __attribute__((always_inline, unused));
|
||||||
static inline int32_t substract_int32_then_divide_int32(int32_t a, int32_t b, int32_t c)
|
static inline int32_t substract_int32_then_divide_int32(int32_t a, int32_t b, int32_t c)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@
|
||||||
#include "utility/dspinst.h"
|
#include "utility/dspinst.h"
|
||||||
|
|
||||||
|
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
|
|
||||||
void AudioSynthWaveformPWM::update(void)
|
void AudioSynthWaveformPWM::update(void)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ void AudioSynthWaveformSine::update(void)
|
||||||
scale = (ph >> 8) & 0xFFFF;
|
scale = (ph >> 8) & 0xFFFF;
|
||||||
val2 *= scale;
|
val2 *= scale;
|
||||||
val1 *= 0x10000 - scale;
|
val1 *= 0x10000 - scale;
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
block->data[i] = multiply_32x32_rshift32(val1 + val2, magnitude);
|
block->data[i] = multiply_32x32_rshift32(val1 + val2, magnitude);
|
||||||
#elif defined(KINETISL)
|
#elif defined(KINETISL)
|
||||||
block->data[i] = (((val1 + val2) >> 16) * magnitude) >> 16;
|
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
|
// High accuracy 11th order Taylor Series Approximation
|
||||||
// input is 0 to 0xFFFFFFFF, representing 0 to 360 degree phase
|
// input is 0 to 0xFFFFFFFF, representing 0 to 360 degree phase
|
||||||
// output is 32 bit signed integer, top 25 bits should be very good
|
// 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)
|
void AudioSynthWaveformSineHires::update(void)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
audio_block_t *msw, *lsw;
|
audio_block_t *msw, *lsw;
|
||||||
uint32_t i, ph, inc;
|
uint32_t i, ph, inc;
|
||||||
int32_t val;
|
int32_t val;
|
||||||
|
|
@ -138,7 +138,7 @@ void AudioSynthWaveformSineHires::update(void)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
|
|
||||||
void AudioSynthWaveformSineModulated::update(void)
|
void AudioSynthWaveformSineModulated::update(void)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -221,7 +221,7 @@ void AudioSynthWaveformModulated::update(void)
|
||||||
n = n << 1;
|
n = n << 1;
|
||||||
#else
|
#else
|
||||||
// exp2 algorithm by Laurent de Soras
|
// 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 = (n + 134217728) << 3;
|
||||||
n = multiply_32x32_rshift32_rounded(n, n);
|
n = multiply_32x32_rshift32_rounded(n, n);
|
||||||
n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
|
n = multiply_32x32_rshift32_rounded(n, 715827883) << 3;
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ void AudioSynthNoiseWhite::update(void)
|
||||||
end = p + AUDIO_BLOCK_SAMPLES/2;
|
end = p + AUDIO_BLOCK_SAMPLES/2;
|
||||||
lo = seed;
|
lo = seed;
|
||||||
do {
|
do {
|
||||||
#if defined(KINETISK)
|
#if defined(__ARM_ARCH_7EM__)
|
||||||
hi = multiply_16bx16t(16807, lo); // 16807 * (lo >> 16)
|
hi = multiply_16bx16t(16807, lo); // 16807 * (lo >> 16)
|
||||||
lo = 16807 * (lo & 0xFFFF);
|
lo = 16807 * (lo & 0xFFFF);
|
||||||
lo += (hi & 0x7FFF) << 16;
|
lo += (hi & 0x7FFF) << 16;
|
||||||
|
|
|
||||||
|
|
@ -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) __attribute__((always_inline, unused));
|
||||||
static inline int32_t signed_saturate_rshift(int32_t val, int bits, int rshift)
|
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;
|
int32_t out;
|
||||||
asm volatile("ssat %0, %1, %2, asr %3" : "=r" (out) : "I" (bits), "r" (val), "I" (rshift));
|
asm volatile("ssat %0, %1, %2, asr %3" : "=r" (out) : "I" (bits), "r" (val), "I" (rshift));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline int16_t saturate16(int32_t val)
|
static inline int16_t saturate16(int32_t val)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined (__ARM_ARCH_7EM__)
|
||||||
int16_t out;
|
int16_t out;
|
||||||
int32_t tmp;
|
int32_t tmp;
|
||||||
asm volatile("ssat %0, %1, %2" : "=r" (tmp) : "I" (16), "r" (val) );
|
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) __attribute__((always_inline, unused));
|
||||||
static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b)
|
static inline int32_t signed_multiply_32x16b(int32_t a, uint32_t b)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined (__ARM_ARCH_7EM__)
|
||||||
int32_t out;
|
int32_t out;
|
||||||
asm volatile("smulwb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
asm volatile("smulwb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b)
|
static inline int32_t signed_multiply_32x16t(int32_t a, uint32_t b)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined (__ARM_ARCH_7EM__)
|
||||||
int32_t out;
|
int32_t out;
|
||||||
asm volatile("smulwt %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
asm volatile("smulwt %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline int32_t multiply_32x32_rshift32(int32_t a, int32_t b)
|
static inline int32_t multiply_32x32_rshift32(int32_t a, int32_t b)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined (__ARM_ARCH_7EM__)
|
||||||
int32_t out;
|
int32_t out;
|
||||||
asm volatile("smmul %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
asm volatile("smmul %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline int32_t multiply_32x32_rshift32_rounded(int32_t a, int32_t b)
|
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;
|
int32_t out;
|
||||||
asm volatile("smmulr %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
asm volatile("smmulr %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline int32_t multiply_accumulate_32x32_rshift32_rounded(int32_t sum, int32_t a, int32_t b)
|
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;
|
int32_t out;
|
||||||
asm volatile("smmlar %0, %2, %3, %1" : "=r" (out) : "r" (sum), "r" (a), "r" (b));
|
asm volatile("smmlar %0, %2, %3, %1" : "=r" (out) : "r" (sum), "r" (a), "r" (b));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline int32_t multiply_subtract_32x32_rshift32_rounded(int32_t sum, int32_t a, int32_t b)
|
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;
|
int32_t out;
|
||||||
asm volatile("smmlsr %0, %2, %3, %1" : "=r" (out) : "r" (sum), "r" (a), "r" (b));
|
asm volatile("smmlsr %0, %2, %3, %1" : "=r" (out) : "r" (sum), "r" (a), "r" (b));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline uint32_t pack_16t_16t(int32_t a, int32_t b)
|
static inline uint32_t pack_16t_16t(int32_t a, int32_t b)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined (__ARM_ARCH_7EM__)
|
||||||
int32_t out;
|
int32_t out;
|
||||||
asm volatile("pkhtb %0, %1, %2, asr #16" : "=r" (out) : "r" (a), "r" (b));
|
asm volatile("pkhtb %0, %1, %2, asr #16" : "=r" (out) : "r" (a), "r" (b));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline uint32_t pack_16t_16b(int32_t a, int32_t b)
|
static inline uint32_t pack_16t_16b(int32_t a, int32_t b)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined (__ARM_ARCH_7EM__)
|
||||||
int32_t out;
|
int32_t out;
|
||||||
asm volatile("pkhtb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
asm volatile("pkhtb %0, %1, %2" : "=r" (out) : "r" (a), "r" (b));
|
||||||
return out;
|
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) __attribute__((always_inline, unused));
|
||||||
static inline uint32_t pack_16b_16b(int32_t a, int32_t b)
|
static inline uint32_t pack_16b_16b(int32_t a, int32_t b)
|
||||||
{
|
{
|
||||||
#if defined(KINETISK)
|
#if defined (__ARM_ARCH_7EM__)
|
||||||
int32_t out;
|
int32_t out;
|
||||||
asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (out) : "r" (b), "r" (a));
|
asm volatile("pkhbt %0, %1, %2, lsl #16" : "=r" (out) : "r" (b), "r" (a));
|
||||||
return out;
|
return out;
|
||||||
|
|
|
||||||
56
sampler/lib/Audio_SdFat/utility/imxrt_hw.cpp
Normal file
56
sampler/lib/Audio_SdFat/utility/imxrt_hw.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2019, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
(c) Frank B
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1052__) || defined(__IMXRT1062__)
|
||||||
|
#include "imxrt_hw.h"
|
||||||
|
|
||||||
|
PROGMEM
|
||||||
|
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force) // sets PLL4
|
||||||
|
{
|
||||||
|
if (!force && (CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_ENABLE)) return;
|
||||||
|
|
||||||
|
CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_BYPASS | CCM_ANALOG_PLL_AUDIO_ENABLE
|
||||||
|
| CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2) // 2: 1/4; 1: 1/2; 0: 1/1
|
||||||
|
| CCM_ANALOG_PLL_AUDIO_DIV_SELECT(nfact);
|
||||||
|
|
||||||
|
CCM_ANALOG_PLL_AUDIO_NUM = nmult & CCM_ANALOG_PLL_AUDIO_NUM_MASK;
|
||||||
|
CCM_ANALOG_PLL_AUDIO_DENOM = ndiv & CCM_ANALOG_PLL_AUDIO_DENOM_MASK;
|
||||||
|
|
||||||
|
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_POWERDOWN;//Switch on PLL
|
||||||
|
while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {}; //Wait for pll-lock
|
||||||
|
|
||||||
|
const int div_post_pll = 1; // other values: 2,4
|
||||||
|
CCM_ANALOG_MISC2 &= ~(CCM_ANALOG_MISC2_DIV_MSB | CCM_ANALOG_MISC2_DIV_LSB);
|
||||||
|
if(div_post_pll>1) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_LSB;
|
||||||
|
if(div_post_pll>3) CCM_ANALOG_MISC2 |= CCM_ANALOG_MISC2_DIV_MSB;
|
||||||
|
|
||||||
|
CCM_ANALOG_PLL_AUDIO &= ~CCM_ANALOG_PLL_AUDIO_BYPASS;//Disable Bypass
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
48
sampler/lib/Audio_SdFat/utility/imxrt_hw.h
Normal file
48
sampler/lib/Audio_SdFat/utility/imxrt_hw.h
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* Audio Library for Teensy 3.X
|
||||||
|
* Copyright (c) 2019, Paul Stoffregen, paul@pjrc.com
|
||||||
|
*
|
||||||
|
* Development of this audio library was funded by PJRC.COM, LLC by sales of
|
||||||
|
* Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop
|
||||||
|
* open source software by purchasing Teensy or other PJRC products.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice, development funding notice, and this permission
|
||||||
|
* notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
(c) Frank B
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(__IMXRT1062__)
|
||||||
|
|
||||||
|
#ifndef imxr_hw_h_
|
||||||
|
#define imxr_hw_h_
|
||||||
|
|
||||||
|
#define IMXRT_CACHE_ENABLED 2 // 0=disabled, 1=WT, 2= WB
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <imxrt.h>
|
||||||
|
|
||||||
|
void set_audioClock(int nfact, int32_t nmult, uint32_t ndiv, bool force = false); // sets PLL4
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else
|
||||||
|
//No IMXRT
|
||||||
|
#define IMXRT_CACHE_ENABLED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
#if !defined(__IMXRT1052__) && !defined(__IMXRT1062__)
|
||||||
#ifndef pdb_h_
|
#ifndef pdb_h_
|
||||||
#define pdb_h_
|
#define pdb_h_
|
||||||
|
|
||||||
|
|
@ -71,3 +71,4 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
@ -15,18 +15,42 @@ env_default = teensy35
|
||||||
lib_ignore = Audio, SD
|
lib_ignore = Audio, SD
|
||||||
lib_deps =
|
lib_deps =
|
||||||
721@3.0.2 ; TaskScheduler
|
721@3.0.2 ; TaskScheduler
|
||||||
322@1.0.7 ; SdFat
|
322 ; SdFat
|
||||||
|
|
||||||
[env:teensy35]
|
[env:teensy35]
|
||||||
platform = teensy@3.6.0
|
platform = teensy
|
||||||
board = teensy35
|
board = teensy35
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps = ${common.lib_deps}
|
||||||
lib_ignore = ${common.lib_ignore}
|
lib_ignore = ${common.lib_ignore}
|
||||||
|
|
||||||
[env:teensy36]
|
[env:teensy36]
|
||||||
platform = teensy@3.6.0
|
platform = teensy
|
||||||
board = teensy36
|
board = teensy36
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps = ${common.lib_deps}
|
lib_deps = ${common.lib_deps}
|
||||||
lib_ignore = ${common.lib_ignore}
|
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}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue