Source file: /~heha/Mikrocontroller/Sprache/sprich.zip/talkie.cpp

// Talkie-Bibliothek ursprünglich Copyright 2011 Peter Knight
// Umgesetzt auf Synchron, avr-gcc und ATmega32U4 2021 H. Haftmann
// Lautsprecher-Ausgang hier festgelegt auf 2 komplementäre
// High-Speed-Timer4-Ausgänge !OC4B und OC4B = PB5 und PB6.
// Bei 64 MHz und 8-Bit-Ausgabe erreicht dieser 250 kHz Pulsrate.
// Mit fantastischen 11 Bit Auflösung wären immer noch 62,5 kHz drin,
// bei energiesparender 32-MHz-Taktung auch noch unhörbare 31,25 kHz.
// Alle Konstanten im Flash. FIFO-Puffer vor der Sampleausgabe.
// Kein Objekt sondern Namespace. Gestraffte, logische say()-Funktion.
// Versuchsweise Implementierung der Multiplikation ohne mul-Befehl
// funktioniert einigermaßen.

#include "talkie.h"
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

static const unsigned FS=8000;	// Abtastrate des Spachgenerators in Hz
#define elemof(x) (sizeof(x)/sizeof(*(x)))

static struct Synth{
  byte Period;
  unsigned Energy;
  unsigned Rand;
  char K[10];
}synth;

static const byte tmsEnergy[] PROGMEM = {2,3,4,5,7,10,15,20,32,41,57,81,114,161};
static const byte tmsPeriod[0x40] PROGMEM = {
	0x00,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,
	0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2D,0x2F,0x31,
	0x33,0x35,0x36,0x39,0x3B,0x3D,0x3F,0x42,0x45,0x47,0x49,0x4D,0x4F,0x51,0x55,0x57,
	0x5C,0x5F,0x63,0x66,0x6A,0x6E,0x73,0x77,0x7B,0x80,0x85,0x8A,0x8F,0x95,0x9A,0xA0};
static const char tmsK0[0x20] PROGMEM = {
	-126,-125,-125,-124,-124,-123,-122,-121,-120,-119,-118,-116,-115,-113,-112,-110,
	-103, -95, -85, -72, -57, -40, -21,   0,  20,  39,  56,  71,  84,  94, 103, 109};
static const byte tmsK1[0x20] PROGMEM = {
	0xAE,0xB4,0xBB,0xC3,0xCB,0xD4,0xDD,0xE7,0xF1,0xFB,0x06,0x10,0x1A,0x24,0x2D,0x36,
	0x3E,0x45,0x4C,0x53,0x58,0x5D,0x62,0x66,0x69,0x6C,0x6F,0x71,0x73,0x75,0x77,0x7E};
static const byte tmsK2[0x10] PROGMEM = {
	0x92,0x9F,0xAD,0xBA,0xC8,0xD5,0xE3,0xF0,0xFE,0x0B,0x19,0x26,0x34,0x41,0x4F,0x5C};
static const byte tmsK3[0x10] PROGMEM = {
	0xAE,0xBC,0xCA,0xD8,0xE6,0xF4,0x01,0x0F,0x1D,0x2B,0x39,0x47,0x55,0x63,0x71,0x7E};
static const byte tmsK4[0x10] PROGMEM = {
	0xAE,0xBA,0xC5,0xD1,0xDD,0xE8,0xF4,0xFF,0x0B,0x17,0x22,0x2E,0x39,0x45,0x51,0x5C};
static const byte tmsK5[0x10] PROGMEM = {
	0xC0,0xCB,0xD6,0xE1,0xEC,0xF7,0x03,0x0E,0x19,0x24,0x2F,0x3A,0x45,0x50,0x5B,0x66};
static const byte tmsK6[0x10] PROGMEM = {
	0xB3,0xBF,0xCB,0xD7,0xE3,0xEF,0xFB,0x07,0x13,0x1F,0x2B,0x37,0x43,0x4F,0x5A,0x66};
static const byte tmsK7[0x08] PROGMEM = {0xC0,0xD8,0xF0,0x07,0x1F,0x37,0x4F,0x66};
static const byte tmsK8[0x08] PROGMEM = {0xC0,0xD4,0xE8,0xFC,0x10,0x25,0x39,0x4D};
static const byte tmsK9[0x08] PROGMEM = {0xCD,0xDF,0xF1,0x04,0x16,0x20,0x3B,0x4D};


static volatile byte fifo[16],fifor,fifow;

ISR(TIMER1_COMPA_vect) {
  const volatile byte*r=fifo+fifor;
  OCR4B=*r;
  fifor=(r-fifo+1)&15;
}

extern "C" int fmul8x16(char a,int b);// {return (long)a*b>>7;}

static void synthsample() {
  static byte periodCounter;
  static int x[10];
  static int u[11];

  if (synth.Period) {
    // Voiced source
    if (periodCounter < synth.Period) {
      periodCounter++;
    } else {
      periodCounter = 0;
    }
    static const byte chirp[] PROGMEM = {
	0x00,0x2a,0xd4,0x32,0xb2,0x12,0x25,0x14,0x02,0xe1,0xc5,0x02,0x5f,0x5a,0x05,0x0f,
	0x26,0xfc,0xa5,0xa5,0xd6,0xdd,0xdc,0xfc,0x25,0x2b,0x22,0x21,0x0f,0xff,0xf8,0xee,
	0xed,0xef,0xf7,0xf6,0xfa,0x00,0x03,0x02,0x01};
    if (periodCounter < elemof(chirp)) {
      u[10] = (((char)pgm_read_byte(chirp+periodCounter)) * (uint32_t) synth.Energy) >> 8;
    } else {
      u[10] = 0;
    }
  } else {
    // Unvoiced source
    synth.Rand = (synth.Rand >> 1) ^ ((synth.Rand & 1) ? 0xB800 : 0);
    u[10] = (synth.Rand & 1) ? synth.Energy : -synth.Energy;
  }
  // Lattice filter forward path
  for (auto i=10; --i>=0; ) u[i] = u[i+1]- fmul8x16(synth.K[i],x[i]);

  // Output clamp
  if (u[0] > 511) u[0] = 511;
  if (u[0] < -511) u[0] = -511;
  
  // Lattice filter reverse path
  for (auto i=9; --i>=0; ) x[i+1] = x[i] + fmul8x16(synth.K[i],u[i]);
  x[0] = u[0];

  byte w=fifow, wn=(w+1)&15;
  while (wn==fifor) Talkie::idle();	// Warte bis Sample-FIFO Platz hat
  fifo[w] = (u[0]>>2)+0x80;
  fifow = wn;
}

const byte*ptrAddr;
byte ptrBit;

static void setPtr(const byte* addr) {
  ptrAddr = addr;
  ptrBit = 1;
}

extern "C" byte getBits(byte bits);

void Talkie::idle() {
  PORTD |= 0x20;	// TX-LED aus
  sleep_cpu();
  PORTD &=~0x20;	// TX-LED ein
}

void Talkie::say(const byte* addr) {
// Die PLL sollte bereits für High-Speed-Timer4 aktiviert sein!
  TCCR4A= 0x11;	// PWM an OC4B
  TCCR4B= 0x01;	// aktivieren mit Vorteiler 1: 64 MHz / 256 = 250 kHz
  TCCR1B= 0x09;	// CTC-Modus mit OCR1A = Zählumfang
  OCR1A = (F_CPU/FS)-1;	// 8 kHz
  TIMSK1= 0x02;	// Interruptfreigabe
  DDRB |= 0x60;	// B5 und B6 = Arduino 9 und 10 auf Ausgang

  setPtr(addr);
  sei();
  synth.Rand = 1;
  for(;;) {
		// Read speech data, processing the variable size frames.
    byte energy = getBits(4);
    if (!energy) synth.Energy = 0;	// Energy = 0: rest frame
    else if (energy == 0xf) break;
    else{
      synth.Energy = pgm_read_byte(tmsEnergy+energy-1);
      bool repeat = getBits(1);
      synth.Period = pgm_read_byte(tmsPeriod+getBits(6));
      if (!repeat) {
	synth.K[0] = (char)pgm_read_byte(tmsK0+getBits(5));
	synth.K[1] = (char)pgm_read_byte(tmsK1+getBits(5));
	synth.K[2] = (char)pgm_read_byte(tmsK2+getBits(4));
	synth.K[3] = (char)pgm_read_byte(tmsK3+getBits(4));
	if (synth.Period) {	// Stimmhafte Rahmen benutzen 6 Extra-Koeffizienten.
	  synth.K[4] = (char)pgm_read_byte(tmsK4+getBits(4));
	  synth.K[5] = (char)pgm_read_byte(tmsK5+getBits(4));
	  synth.K[6] = (char)pgm_read_byte(tmsK6+getBits(4));
	  synth.K[7] = (char)pgm_read_byte(tmsK7+getBits(3));
	  synth.K[8] = (char)pgm_read_byte(tmsK8+getBits(3));
	  synth.K[9] = (char)pgm_read_byte(tmsK9+getBits(3));
	}
      }
    }
    byte cx=FS/40;
    do synthsample(); while(--cx);
  }
  DDRB &= ~0x60;
  TCCR4B= 0;
  TIMSK1= 0;
}
Detected encoding: UTF-80