From 78b7eb75ae6877bb67c7c3d2f99a9bd3332e4921 Mon Sep 17 00:00:00 2001 From: mescalinum Date: Thu, 19 Aug 2010 20:03:23 +0000 Subject: TMS5220 chip emulation svn path=/trunk/externals/ffext/; revision=13845 --- tms5220~/Makefile | 297 ++++++++++++++++++ tms5220~/README.txt | 6 + tms5220~/tms5220/driver.h | 0 tms5220~/tms5220/tms5220.c | 701 +++++++++++++++++++++++++++++++++++++++++++ tms5220~/tms5220/tms5220.h | 15 + tms5220~/tms5220/tms5220.txt | 86 ++++++ tms5220~/tms5220/tms5220r.c | 105 +++++++ tms5220~/tms5220~-help.pd | 30 ++ tms5220~/tms5220~-meta.pd | 8 + tms5220~/tms5220~.c | 120 ++++++++ 10 files changed, 1368 insertions(+) create mode 100644 tms5220~/Makefile create mode 100644 tms5220~/README.txt create mode 100644 tms5220~/tms5220/driver.h create mode 100644 tms5220~/tms5220/tms5220.c create mode 100644 tms5220~/tms5220/tms5220.h create mode 100644 tms5220~/tms5220/tms5220.txt create mode 100644 tms5220~/tms5220/tms5220r.c create mode 100644 tms5220~/tms5220~-help.pd create mode 100644 tms5220~/tms5220~-meta.pd create mode 100644 tms5220~/tms5220~.c (limited to 'tms5220~') diff --git a/tms5220~/Makefile b/tms5220~/Makefile new file mode 100644 index 0000000..4c72e4d --- /dev/null +++ b/tms5220~/Makefile @@ -0,0 +1,297 @@ +## Pd library template version 1.0.1 +# For instructions on how to use this template, see: +# http://puredata.info/docs/developer/MakefileTemplate +LIBRARY_NAME = tms5220~ + +# add your .c source files to the SOURCES variable, help files will be +# included automatically +SOURCES = tms5220~.c + +# For objects that only build on certain platforms, add those to the SOURCES +# line for the right platforms. +SOURCES_android = +SOURCES_cygwin = +SOURCES_macosx = +SOURCES_iphoneos = +SOURCES_linux = +SOURCES_windows = + +# list all pd objects (i.e. myobject.pd) files here, and their helpfiles will +# be included automatically +PDOBJECTS = + +# example patches and related files, in the 'examples' subfolder +EXAMPLES = + +# manuals and related files, in the 'manual' subfolder +MANUAL = + +# if you want to include any other files in the source and binary tarballs, +# list them here. This can be anything from header files, example patches, +# documentation, etc. README.txt and LICENSE.txt are required and therefore +# automatically included +EXTRA_DIST = + + + +#------------------------------------------------------------------------------# +# +# you shouldn't need to edit anything below here, if we did it right :) +# +#------------------------------------------------------------------------------# + +# get library version from meta file +LIBRARY_VERSION = $(shell sed -n 's|^\#X text [0-9][0-9]* [0-9][0-9]* VERSION \(.*\);|\1|p' $(LIBRARY_NAME)-meta.pd) + +# where Pd lives +PD_PATH = ../../pd +# where to install the library +prefix = /usr/local +libdir = $(prefix)/lib +pkglibdir = $(libdir)/pd-externals +objectsdir = $(pkglibdir) + + +INSTALL = install +INSTALL_FILE = $(INSTALL) -p -m 644 +INSTALL_DIR = $(INSTALL) -p -m 755 -d + +CFLAGS = -DPD -I$(PD_PATH)/src -Wall -W -g +LDFLAGS = +LIBS = +ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \ + $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows) + +DISTDIR=$(LIBRARY_NAME)-$(LIBRARY_VERSION) +ORIGDIR=pd-$(LIBRARY_NAME)_$(LIBRARY_VERSION) + +UNAME := $(shell uname -s) +ifeq ($(UNAME),Darwin) + CPU := $(shell uname -p) + ifeq ($(CPU),arm) # iPhone/iPod Touch + SOURCES += $(SOURCES_iphoneos) + EXTENSION = pd_darwin + OS = iphoneos + IPHONE_BASE=/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin + CC=$(IPHONE_BASE)/gcc + CPP=$(IPHONE_BASE)/cpp + CXX=$(IPHONE_BASE)/g++ + ISYSROOT = -isysroot /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk + IPHONE_CFLAGS = -miphoneos-version-min=3.0 $(ISYSROOT) -arch armv6 + OPT_CFLAGS = -fast -funroll-loops -fomit-frame-pointer + CFLAGS := $(IPHONE_CFLAGS) $(OPT_CFLAGS) $(CFLAGS) \ + -I/Applications/Pd-extended.app/Contents/Resources/include + LDFLAGS += -arch armv6 -bundle -undefined dynamic_lookup $(ISYSROOT) + LIBS += -lc + STRIP = strip -x + DISTBINDIR=$(DISTDIR)-$(OS) + else # Mac OS X + SOURCES += $(SOURCES_macosx) + EXTENSION = pd_darwin + OS = macosx + OPT_CFLAGS = -ftree-vectorize -ftree-vectorizer-verbose=2 -fast +# build universal 32-bit on 10.4 and 32/64 on newer + ifeq ($(shell uname -r | sed 's|\([0-9][0-9]*\)\.[0-9][0-9]*\.[0-9][0-9]*|\1|'), 8) + FAT_FLAGS = -arch ppc -arch i386 -mmacosx-version-min=10.4 + else + FAT_FLAGS = -arch ppc -arch i386 -arch x86_64 -mmacosx-version-min=10.4 + SOURCES += $(SOURCES_iphoneos) + endif + CFLAGS += $(FAT_FLAGS) -fPIC -I/sw/include \ + -I/Applications/Pd-extended.app/Contents/Resources/include + LDFLAGS += $(FAT_FLAGS) -bundle -undefined dynamic_lookup -L/sw/lib + # if the 'pd' binary exists, check the linking against it to aid with stripping + LDFLAGS += $(shell test -e $(PD_PATH)/bin/pd && echo -bundle_loader $(PD_PATH)/bin/pd) + LIBS += -lc + STRIP = strip -x + DISTBINDIR=$(DISTDIR)-$(OS) +# install into ~/Library/Pd on Mac OS X since /usr/local isn't used much + pkglibdir=$(HOME)/Library/Pd + endif +endif +ifeq ($(UNAME),Linux) + SOURCES += $(SOURCES_linux) + EXTENSION = pd_linux + OS = linux + OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer + CFLAGS += -fPIC + LDFLAGS += -Wl,--export-dynamic -shared -fPIC + LIBS += -lc + STRIP = strip --strip-unneeded -R .note -R .comment + DISTBINDIR=$(DISTDIR)-$(OS)-$(shell uname -m) +endif +ifeq (CYGWIN,$(findstring CYGWIN,$(UNAME))) + SOURCES += $(SOURCES_cygwin) + EXTENSION = dll + OS = cygwin + OPT_CFLAGS = -O6 -funroll-loops -fomit-frame-pointer + CFLAGS += + LDFLAGS += -Wl,--export-dynamic -shared -L$(PD_PATH)/src + LIBS += -lc -lpd + STRIP = strip --strip-unneeded -R .note -R .comment + DISTBINDIR=$(DISTDIR)-$(OS) +endif +ifeq (MINGW,$(findstring MINGW,$(UNAME))) + SOURCES += $(SOURCES_windows) + EXTENSION = dll + OS = windows + OPT_CFLAGS = -O3 -funroll-loops -fomit-frame-pointer -march=i686 -mtune=pentium4 + CFLAGS += -mms-bitfields + LDFLAGS += -s -shared -Wl,--enable-auto-import + LIBS += -L$(PD_PATH)/src -L$(PD_PATH)/bin -L$(PD_PATH)/obj -lpd -lwsock32 -lkernel32 -luser32 -lgdi32 + STRIP = strip --strip-unneeded -R .note -R .comment + DISTBINDIR=$(DISTDIR)-$(OS) +endif + +CFLAGS += $(OPT_CFLAGS) + + +.PHONY = install libdir_install single_install install-doc install-exec install-examples install-manual clean dist etags + +all: $(SOURCES:.c=.$(EXTENSION)) + +%.o: %.c + $(CC) $(CFLAGS) -o "$*.o" -c "$*.c" + +%.$(EXTENSION): %.o + $(CC) $(LDFLAGS) -o "$*.$(EXTENSION)" "$*.o" $(LIBS) + chmod a-x "$*.$(EXTENSION)" + +# this links everything into a single binary file +$(LIBRARY_NAME): $(SOURCES:.c=.o) $(LIBRARY_NAME).o + $(CC) $(LDFLAGS) -o $(LIBRARY_NAME).$(EXTENSION) $(SOURCES:.c=.o) $(LIBRARY_NAME).o $(LIBS) + chmod a-x $(LIBRARY_NAME).$(EXTENSION) + + +install: libdir_install + +# The meta and help files are explicitly installed to make sure they are +# actually there. Those files are not optional, then need to be there. +libdir_install: $(SOURCES:.c=.$(EXTENSION)) install-doc install-examples install-manual + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(SOURCES))" || (\ + $(INSTALL_FILE) $(SOURCES:.c=.$(EXTENSION)) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) && \ + $(STRIP) $(addprefix $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/,$(SOURCES:.c=.$(EXTENSION)))) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + +# install library linked as single binary +single_install: $(LIBRARY_NAME) install-doc install-exec + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) $(LIBRARY_NAME).$(EXTENSION) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(STRIP) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/$(LIBRARY_NAME).$(EXTENSION) + +install-doc: + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(SOURCES))" || \ + $(INSTALL_FILE) $(SOURCES:.c=-help.pd) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) \ + $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME) + $(INSTALL_FILE) README.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/README.txt + $(INSTALL_FILE) LICENSE.txt $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/LICENSE.txt + +install-examples: + test -z "$(strip $(EXAMPLES))" || \ + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples && \ + for file in $(EXAMPLES); do \ + $(INSTALL_FILE) examples/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/examples; \ + done + +install-manual: + test -z "$(strip $(MANUAL))" || \ + $(INSTALL_DIR) $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual && \ + for file in $(MANUAL); do \ + $(INSTALL_FILE) manual/$$file $(DESTDIR)$(objectsdir)/$(LIBRARY_NAME)/manual; \ + done + + +clean: + -rm -f -- $(SOURCES:.c=.o) + -rm -f -- $(SOURCES:.c=.$(EXTENSION)) + -rm -f -- $(LIBRARY_NAME).o + -rm -f -- $(LIBRARY_NAME).$(EXTENSION) + +distclean: clean + -rm -f -- $(DISTBINDIR).tar.gz + -rm -rf -- $(DISTBINDIR) + -rm -f -- $(DISTDIR).tar.gz + -rm -rf -- $(DISTDIR) + -rm -f -- $(ORIGDIR).tar.gz + -rm -rf -- $(ORIGDIR) + + +$(DISTBINDIR): + $(INSTALL_DIR) $(DISTBINDIR) + +libdir: all $(DISTBINDIR) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTBINDIR) + $(INSTALL_FILE) $(SOURCES) $(DISTBINDIR) + $(INSTALL_FILE) $(SOURCES:.c=-help.pd) $(DISTBINDIR) + test -z "$(strip $(EXTRA_DIST))" || \ + $(INSTALL_FILE) $(EXTRA_DIST) $(DISTBINDIR) +# tar --exclude-vcs -czpf $(DISTBINDIR).tar.gz $(DISTBINDIR) + +$(DISTDIR): + $(INSTALL_DIR) $(DISTDIR) + +$(ORIGDIR): + $(INSTALL_DIR) $(ORIGDIR) + +dist: $(DISTDIR) + $(INSTALL_FILE) Makefile $(DISTDIR) + $(INSTALL_FILE) README.txt $(DISTDIR) + $(INSTALL_FILE) LICENSE.txt $(DISTDIR) + $(INSTALL_FILE) $(LIBRARY_NAME)-meta.pd $(DISTDIR) + test -z "$(strip $(ALLSOURCES))" || \ + $(INSTALL_FILE) $(ALLSOURCES) $(DISTDIR) + test -z "$(strip $(ALLSOURCES))" || \ + $(INSTALL_FILE) $(ALLSOURCES:.c=-help.pd) $(DISTDIR) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS) $(DISTDIR) + test -z "$(strip $(PDOBJECTS))" || \ + $(INSTALL_FILE) $(PDOBJECTS:.pd=-help.pd) $(DISTDIR) + test -z "$(strip $(EXTRA_DIST))" || \ + $(INSTALL_FILE) $(EXTRA_DIST) $(DISTDIR) + test -z "$(strip $(EXAMPLES))" || \ + $(INSTALL_DIR) $(DISTDIR)/examples && \ + for file in $(EXAMPLES); do \ + $(INSTALL_FILE) examples/$$file $(DISTDIR)/examples; \ + done + test -z "$(strip $(MANUAL))" || \ + $(INSTALL_DIR) $(DISTDIR)/manual && \ + for file in $(MANUAL); do \ + $(INSTALL_FILE) manual/$$file $(DISTDIR)/manual; \ + done + tar --exclude-vcs -czpf $(DISTDIR).tar.gz $(DISTDIR) + +# make a Debian source package +dpkg-source: + debclean + make distclean dist + mv $(DISTDIR) $(ORIGDIR) + tar --exclude-vcs -czpf ../$(ORIGDIR).orig.tar.gz $(ORIGDIR) + rm -f -- $(DISTDIR).tar.gz + rm -rf -- $(DISTDIR) $(ORIGDIR) + cd .. && dpkg-source -b $(LIBRARY_NAME) + +etags: + etags *.h $(SOURCES) ../../pd/src/*.[ch] /usr/include/*.h /usr/include/*/*.h + +showsetup: + @echo "PD_PATH: $(PD_PATH)" + @echo "objectsdir: $(objectsdir)" + @echo "LIBRARY_NAME: $(LIBRARY_NAME)" + @echo "LIBRARY_VERSION: $(LIBRARY_VERSION)" + @echo "SOURCES: $(SOURCES)" + @echo "PDOBJECTS: $(PDOBJECTS)" + @echo "ALLSOURCES: $(ALLSOURCES)" + @echo "UNAME: $(UNAME)" + @echo "CPU: $(CPU)" + @echo "pkglibdir: $(pkglibdir)" + @echo "DISTDIR: $(DISTDIR)" + @echo "ORIGDIR: $(ORIGDIR)" diff --git a/tms5220~/README.txt b/tms5220~/README.txt new file mode 100644 index 0000000..ccce2bd --- /dev/null +++ b/tms5220~/README.txt @@ -0,0 +1,6 @@ +TMS5220 IC emulation for pure-data +written by Federico Ferri +Released under GPL-3 license. + +TMS5220 emulation core comes from MAME sources and it's written by Frank Palazzolo and others. + diff --git a/tms5220~/tms5220/driver.h b/tms5220~/tms5220/driver.h new file mode 100644 index 0000000..e69de29 diff --git a/tms5220~/tms5220/tms5220.c b/tms5220~/tms5220/tms5220.c new file mode 100644 index 0000000..1e61979 --- /dev/null +++ b/tms5220~/tms5220/tms5220.c @@ -0,0 +1,701 @@ +/********************************************************************************************** + + TMS5220 simulator + + Written for MAME by Frank Palazzolo + With help from Neill Corlett + Additional tweaking by Aaron Giles + +***********************************************************************************************/ + +#include +#include +#include + +#include "driver.h" +#include "tms5220.h" + + +/* Pull in the ROM tables */ +#include "tms5220r.c" + + +/* these contain data that describes the 128-bit data FIFO */ +#define FIFO_SIZE 16 +static unsigned char fifo[FIFO_SIZE]; +static int fifo_head; +static int fifo_tail; +static int fifo_count; +static int bits_taken; + + +/* these contain global status bits */ +static int speak_external; +static int talk_status; +static int buffer_low; +static int buffer_empty; +static int irq_pin; + +static void (*irq_func)(void); + + +/* these contain data describing the current and previous voice frames */ +static unsigned short old_energy = 0; +static unsigned short old_pitch = 0; +static int old_k[10] = {0,0,0,0,0,0,0,0,0,0}; + +static unsigned short new_energy = 0; +static unsigned short new_pitch = 0; +static int new_k[10] = {0,0,0,0,0,0,0,0,0,0}; + + +/* these are all used to contain the current state of the sound generation */ +static unsigned short current_energy = 0; +static unsigned short current_pitch = 0; +static int current_k[10] = {0,0,0,0,0,0,0,0,0,0}; + +static unsigned short target_energy = 0; +static unsigned short target_pitch = 0; +static int target_k[10] = {0,0,0,0,0,0,0,0,0,0}; + +static int interp_count = 0; /* number of interp periods (0-7) */ +static int sample_count = 0; /* sample number within interp (0-24) */ +static int pitch_count = 0; + +static int u[11] = {0,0,0,0,0,0,0,0,0,0,0}; +static int x[10] = {0,0,0,0,0,0,0,0,0,0}; + +static int randbit = 0; + + +/* Static function prototypes */ +static void process_command (void); +static int extract_bits (int count); +static int parse_frame (int removeit); +static void check_buffer_low (void); +static void cause_interrupt (void); + + +/*#define DEBUG_5220*/ +#ifdef DEBUG_5220 + static FILE *f; +#endif + + +/********************************************************************************************** + + tms5220_reset -- resets the TMS5220 + +***********************************************************************************************/ + +void tms5220_reset (void) +{ + /* initialize the FIFO */ + memset (fifo, 0, sizeof (fifo)); + fifo_head = fifo_tail = fifo_count = bits_taken = 0; + + /* initialize the chip state */ + speak_external = talk_status = buffer_empty = irq_pin = 0; + buffer_low = 1; + + /* initialize the energy/pitch/k states */ + old_energy = new_energy = current_energy = target_energy = 0; + old_pitch = new_pitch = current_pitch = target_pitch = 0; + memset (old_k, 0, sizeof (old_k)); + memset (new_k, 0, sizeof (new_k)); + memset (current_k, 0, sizeof (current_k)); + memset (target_k, 0, sizeof (target_k)); + + /* initialize the sample generators */ + interp_count = sample_count = pitch_count = 0; + randbit = 0; + memset (u, 0, sizeof (u)); + memset (x, 0, sizeof (x)); + + #ifdef DEBUG_5220 + f = fopen ("tms.log", "w"); + #endif +} + + + +/********************************************************************************************** + + tms5220_reset -- reset the TMS5220 + +***********************************************************************************************/ + +void tms5220_set_irq (void (*func)(void)) +{ + irq_func = func; +} + + +/********************************************************************************************** + + tms5220_data_write -- handle a write to the TMS5220 + +***********************************************************************************************/ + +void tms5220_data_write (int data) +{ + /* add this byte to the FIFO */ + if (fifo_count < FIFO_SIZE) + { + fifo[fifo_tail] = data; + fifo_tail = (fifo_tail + 1) % FIFO_SIZE; + fifo_count++; + + #ifdef DEBUG_5220 + if (f) fprintf (f, "Added byte to FIFO (size=%2d)\n", fifo_count); + #endif + } + else + { + #ifdef DEBUG_5220 + if (f) fprintf (f, "Ran out of room in the FIFO!\n"); + #endif + } + + /* update the buffer low state */ + check_buffer_low (); +} + + +/********************************************************************************************** + + tms5220_status_read -- read status from the TMS5220 + + From the data sheet: + bit 0 = TS - Talk Status is active (high) when the VSP is processing speech data. + Talk Status goes active at the initiation of a Speak command or after nine + bytes of data are loaded into the FIFO following a Speak External command. It + goes inactive (low) when the stop code (Energy=1111) is processed, or + immediately by a buffer empty condition or a reset command. + bit 1 = BL - Buffer Low is active (high) when the FIFO buffer is more than half empty. + Buffer Low is set when the "Last-In" byte is shifted down past the half-full + boundary of the stack. Buffer Low is cleared when data is loaded to the stack + so that the "Last-In" byte lies above the half-full boundary and becomes the + ninth data byte of the stack. + bit 2 = BE - Buffer Empty is active (high) when the FIFO buffer has run out of data + while executing a Speak External command. Buffer Empty is set when the last bit + of the "Last-In" byte is shifted out to the Synthesis Section. This causes + Talk Status to be cleared. Speed is terminated at some abnormal point and the + Speak External command execution is terminated. + +***********************************************************************************************/ + +int tms5220_status_read (void) +{ + /* clear the interrupt pin */ + irq_pin = 0; + + #ifdef DEBUG_5220 + if (f) fprintf (f, "Status read: TS=%d BL=%d BE=%d\n", talk_status, buffer_low, buffer_empty); + #endif + + return (talk_status << 7) | (buffer_low << 6) | (buffer_empty << 5); +} + + + +/********************************************************************************************** + + tms5220_ready_read -- returns the ready state of the TMS5220 + +***********************************************************************************************/ + +int tms5220_ready_read (void) +{ + return (fifo_count < FIFO_SIZE-1); +} + + + +/********************************************************************************************** + + tms5220_int_read -- returns the interrupt state of the TMS5220 + +***********************************************************************************************/ + +int tms5220_int_read (void) +{ + return irq_pin; +} + + + +/********************************************************************************************** + + tms5220_process -- fill the buffer with a specific number of samples + +***********************************************************************************************/ + +void tms5220_process(unsigned char *buffer, unsigned int size) +{ + int buf_count=0; + int i, interp_period; + +tryagain: + + /* if we're not speaking, parse commands */ + while (!speak_external && fifo_count > 0) + process_command (); + + /* if there's nothing to do, bail */ + if (!size) + return; + + /* if we're empty and still not speaking, fill with nothingness */ + if (!speak_external) + goto empty; + + /* if we're to speak, but haven't started, wait for the 9th byte */ + if (!talk_status) + { + if (fifo_count < 9) + goto empty; + + /* parse but don't remove the first frame, and set the status to 1 */ + parse_frame (0); + talk_status = 1; + buffer_empty = 0; + } + + /* loop until the buffer is full or we've stopped speaking */ + while ((size > 0) && speak_external) + { + int current_val; + + /* if we're ready for a new frame */ + if ((interp_count == 0) && (sample_count == 0)) + { + /* Parse a new frame */ + if (!parse_frame (1)) + break; + + /* Set old target as new start of frame */ + current_energy = old_energy; + current_pitch = old_pitch; + for (i = 0; i < 10; i++) + current_k[i] = old_k[i]; + + /* is this a zero energy frame? */ + if (current_energy == 0) + { + //printf("processing frame: zero energy\n"); + target_energy = 0; + target_pitch = current_pitch; + for (i = 0; i < 10; i++) + target_k[i] = current_k[i]; + } + + /* is this a stop frame? */ + else if (current_energy == (energytable[15] >> 6)) + { + //printf("processing frame: stop frame\n"); + current_energy = energytable[0] >> 6; + target_energy = current_energy; + speak_external = talk_status = 0; + interp_count = sample_count = pitch_count = 0; + + /* generate an interrupt if necessary */ + cause_interrupt (); + + /* try to fetch commands again */ + goto tryagain; + } + else + { + /* is this the ramp down frame? */ + if (new_energy == (energytable[15] >> 6)) + { + //printf("processing frame: ramp down\n"); + target_energy = 0; + target_pitch = current_pitch; + for (i = 0; i < 10; i++) + target_k[i] = current_k[i]; + } + /* Reset the step size */ + else + { + //printf("processing frame: Normal\n"); + //printf("*** Energy = %d\n",current_energy); + //printf("proc: %d %d\n",last_fbuf_head,fbuf_head); + + target_energy = new_energy; + target_pitch = new_pitch; + + for (i = 0; i < 4; i++) + target_k[i] = new_k[i]; + if (current_pitch == 0) + for (i = 4; i < 10; i++) + { + target_k[i] = current_k[i] = 0; + } + else + for (i = 4; i < 10; i++) + target_k[i] = new_k[i]; + } + } + } + else if (interp_count == 0) + { + /* Update values based on step values */ + //printf("\n"); + + interp_period = sample_count / 25; + current_energy += (target_energy - current_energy) / interp_coeff[interp_period]; + if (old_pitch != 0) + current_pitch += (target_pitch - current_pitch) / interp_coeff[interp_period]; + + //printf("*** Energy = %d\n",current_energy); + + for (i = 0; i < 10; i++) + { + current_k[i] += (target_k[i] - current_k[i]) / interp_coeff[interp_period]; + } + } + + if (old_energy == 0) + { + /* generate silent samples here */ + current_val = 0x00; + } + else if (old_pitch == 0) + { + /* generate unvoiced samples here */ + randbit = (rand () % 2) * 2 - 1; + current_val = (randbit * current_energy) / 4; + } + else + { + /* generate voiced samples here */ + if (pitch_count < sizeof (chirptable)) + current_val = (chirptable[pitch_count] * current_energy) / 256; + else + current_val = 0x00; + } + + /* Lattice filter here */ + + u[10] = current_val; + + for (i = 9; i >= 0; i--) + { + u[i] = u[i+1] - ((current_k[i] * x[i]) / 32768); + } + for (i = 9; i >= 1; i--) + { + x[i] = x[i-1] + ((current_k[i-1] * u[i-1]) / 32768); + } + + x[0] = u[0]; + + /* clipping, just like the chip */ + + if (u[0] > 511) + buffer[buf_count] = 127; + else if (u[0] < -512) + buffer[buf_count] = -128; + else + buffer[buf_count] = u[0] >> 2; + + /* Update all counts */ + + size--; + sample_count = (sample_count + 1) % 200; + + if (current_pitch != 0) + pitch_count = (pitch_count + 1) % current_pitch; + else + pitch_count = 0; + + interp_count = (interp_count + 1) % 25; + buf_count++; + } + +empty: + + while (size > 0) + { + buffer[buf_count] = 0x00; + buf_count++; + size--; + } +} + + + +/********************************************************************************************** + + process_command -- extract a byte from the FIFO and interpret it as a command + +***********************************************************************************************/ + +static void process_command (void) +{ + unsigned char cmd; + + /* if there are stray bits, ignore them */ + if (bits_taken) + { + bits_taken = 0; + fifo_count--; + fifo_head = (fifo_head + 1) % FIFO_SIZE; + } + + /* grab a full byte from the FIFO */ + if (fifo_count > 0) + { + cmd = fifo[fifo_head] & 0x70; + fifo_count--; + fifo_head = (fifo_head + 1) % FIFO_SIZE; + + /* only real command we handle now is speak external */ + if (cmd == 0x60) + { + speak_external = 1; + + /* according to the datasheet, this will cause an interrupt due to a BE condition */ + if (!buffer_empty) + { + buffer_empty = 1; + cause_interrupt (); + } + } + } + + /* update the buffer low state */ + check_buffer_low (); +} + + + +/********************************************************************************************** + + extract_bits -- extract a specific number of bits from the FIFO + +***********************************************************************************************/ + +static int extract_bits (int count) +{ + int val = 0; + + while (count--) + { + val = (val << 1) | ((fifo[fifo_head] >> bits_taken) & 1); + bits_taken++; + if (bits_taken >= 8) + { + fifo_count--; + fifo_head = (fifo_head + 1) % FIFO_SIZE; + bits_taken = 0; + } + } + return val; +} + + + +/********************************************************************************************** + + parse_frame -- parse a new frame's worth of data; returns 0 if not enough bits in buffer + +***********************************************************************************************/ + +static int parse_frame (int removeit) +{ + int old_head, old_taken, old_count; + int bits, indx, i, rep_flag; + + /* remember previous frame */ + old_energy = new_energy; + old_pitch = new_pitch; + for (i = 0; i < 10; i++) + old_k[i] = new_k[i]; + + /* clear out the new frame */ + new_energy = 0; + new_pitch = 0; + for (i = 0; i < 10; i++) + new_k[i] = 0; + + /* if the previous frame was a stop frame, don't do anything */ + if (old_energy == (energytable[15] >> 6)) + return 1; + + /* remember the original FIFO counts, in case we don't have enough bits */ + old_count = fifo_count; + old_head = fifo_head; + old_taken = bits_taken; + + /* count the total number of bits available */ + bits = fifo_count * 8 - bits_taken; + + /* attempt to extract the energy index */ + bits -= 4; + if (bits < 0) + goto ranout; + indx = extract_bits (4); + new_energy = energytable[indx] >> 6; + + /* if the index is 0 or 15, we're done */ + if (indx == 0 || indx == 15) + { + #ifdef DEBUG_5220 + if (f) fprintf (f, " (4-bit energy=%d frame)\n",new_energy); + #endif + + /* clear fifo if stop frame encountered */ + if (indx == 15) + { + fifo_head = fifo_tail = fifo_count = bits_taken = 0; + removeit = 1; + } + goto done; + } + + /* attempt to extract the repeat flag */ + bits -= 1; + if (bits < 0) + goto ranout; + rep_flag = extract_bits (1); + + /* attempt to extract the pitch */ + bits -= 6; + if (bits < 0) + goto ranout; + indx = extract_bits (6); + new_pitch = pitchtable[indx] / 256; + + /* if this is a repeat frame, just copy the k's */ + if (rep_flag) + { + for (i = 0; i < 10; i++) + new_k[i] = old_k[i]; + + #ifdef DEBUG_5220 + if (f) fprintf (f, " (11-bit energy=%d pitch=%d rep=%d frame)\n", new_energy, new_pitch, rep_flag); + #endif + goto done; + } + + /* if the pitch index was zero, we need 4 k's */ + if (indx == 0) + { + /* attempt to extract 4 K's */ + bits -= 18; + if (bits < 0) + goto ranout; + new_k[0] = k1table[extract_bits (5)]; + new_k[1] = k2table[extract_bits (5)]; + new_k[2] = k3table[extract_bits (4)]; + new_k[3] = k4table[extract_bits (4)]; + + #ifdef DEBUG_5220 + if (f) fprintf (f, " (29-bit energy=%d pitch=%d rep=%d 4K frame)\n", new_energy, new_pitch, rep_flag); + #endif + goto done; + } + + /* else we need 10 K's */ + bits -= 39; + if (bits < 0) + goto ranout; + new_k[0] = k1table[extract_bits (5)]; + new_k[1] = k2table[extract_bits (5)]; + new_k[2] = k3table[extract_bits (4)]; + new_k[3] = k4table[extract_bits (4)]; + new_k[4] = k5table[extract_bits (4)]; + new_k[5] = k6table[extract_bits (4)]; + new_k[6] = k7table[extract_bits (4)]; + new_k[7] = k8table[extract_bits (3)]; + new_k[8] = k9table[extract_bits (3)]; + new_k[9] = k10table[extract_bits (3)]; + + #ifdef DEBUG_5220 + if (f) fprintf (f, " (50-bit energy=%d pitch=%d rep=%d 10K frame)\n", new_energy, new_pitch, rep_flag); + #endif + +done: + + #ifdef DEBUG_5220 + if (f) fprintf (f, "Parsed a frame successfully - %d bits remaining\n", bits); + #endif + + /* if we're not to remove this one, restore the FIFO */ + if (!removeit) + { + fifo_count = old_count; + fifo_head = old_head; + bits_taken = old_taken; + } + + /* update the buffer_low status */ + check_buffer_low (); + return 1; + +ranout: + + #ifdef DEBUG_5220 + if (f) fprintf (f, "Ran out of bits on a parse!\n"); + #endif + + /* this is an error condition; mark the buffer empty and turn off speaking */ + buffer_empty = 1; + talk_status = speak_external = 0; + fifo_count = fifo_head = fifo_tail = 0; + + /* generate an interrupt if necessary */ + cause_interrupt (); + return 0; +} + + + +/********************************************************************************************** + + check_buffer_low -- check to see if the buffer low flag should be on or off + +***********************************************************************************************/ + +static void check_buffer_low (void) +{ + /* did we just become low? */ + if (fifo_count < 8) + { + /* generate an interrupt if necessary */ + if (!buffer_low) + cause_interrupt (); + buffer_low = 1; + + #ifdef DEBUG_5220 + if (f) fprintf (f, "Buffer low set\n"); + #endif + } + + /* did we just become full? */ + else + { + buffer_low = 0; + + #ifdef DEBUG_5220 + if (f) fprintf (f, "Buffer low cleared\n"); + #endif + } +} + + + +/********************************************************************************************** + + cause_interrupt -- generate an interrupt + +***********************************************************************************************/ + +static void cause_interrupt (void) +{ + irq_pin = 1; + if (irq_func) irq_func (); +} diff --git a/tms5220~/tms5220/tms5220.h b/tms5220~/tms5220/tms5220.h new file mode 100644 index 0000000..614d7bc --- /dev/null +++ b/tms5220~/tms5220/tms5220.h @@ -0,0 +1,15 @@ +#ifndef tms5220_h +#define tms5220_h + +void tms5220_reset (void); +void tms5220_set_irq (void (*func)(void)); + +void tms5220_data_write (int data); +int tms5220_status_read (void); +int tms5220_ready_read (void); +int tms5220_int_read (void); + +void tms5220_process (unsigned char *buffer, unsigned int size); + +#endif + diff --git a/tms5220~/tms5220/tms5220.txt b/tms5220~/tms5220/tms5220.txt new file mode 100644 index 0000000..8af747c --- /dev/null +++ b/tms5220~/tms5220/tms5220.txt @@ -0,0 +1,86 @@ +***************************** + + TI TMS5220 Emulator + (c) Frank Palazzolo + +***************************** + +The TI TMS5220 Speech chip uses Linear-Predictive decoding scheme to produce +speech from very compact data. This scheme is very similar to the U.S. +Federal Standard LPC-10e coding system, which was developed soon after +this chip. + +It is virtually identical to the chip used in the landmark "Speak 'N Spell" +toy produced in the '70s. + +Acknowledgements: +----------------- + +I would like to thank Larry Brantingham, the original designer of the chip, +for his technical help. + +I would also like to thank Neill Cortlett, who first showed that this chip +could be emulated in real-time in his Multi-Gauntlet Emulator. + +Theory of operation: +-------------------- + +The TI speech chip contains a 128-bit parallel-in, serial-out FIFO, a +10-pole digital lattice filter which models the vocal tract, and a D/A +converter. It was originally design to operate either with a microcomputer +interface, or with a serial speech ROM. The Speech ROM functionality is +not emulated as no arcade games require it. + +The input data is writen a byte at a time into the chip, and it is +decoded bitwise into variable length frames. + +Possible Frame Types are as follows: + + Energy RF Pitch K1 K2 K3 K4 K5 K6 K7 K8 K9 K10 + ----------------------------------------------------------------- + +Silent 0000 +Stop 1111 +Repeat XXXX 1 XXXXXX +Unvoiced XXXX 0 000000 XXXXX XXXXX XXXX XXXX +Voiced XXXX 0 XXXXXX XXXXX XXXXX XXXX XXXX XXXX XXXX XXXX XXX XXX XXX + +Stop Frame: Stops the current speech +Repeat Frame: Uses the digital filter coefficients from the previous frame, + with new Energy and Pitch values +Unvoiced Frame: Uses Noise generator to feed 4 pole digital filter + (All other coefficients are set to zero) +Voiced Frame: Uses Pulse Generator to feed 10 pole digital filter + +All parameters (Energy, Pitch, K1-K10) are indexes into a lookup table for +actual values (see TMS5220R.c) + +K1-K10 are reflection coefficients for the lattice filter. + +Each frame is used to generate 200 samples, and 8 times during each frame, +(every 25 samples), these values are linearly interpolated to smooth out +frame transitions. + +The Noise generator is based on a shift-register type random-bit generator. +The Pulse generator is based on a lookup table. + +API: +---- + +TBD + +More: +----- + +For further technical info, the data sheet is floating around on the net. +(I believe the name is TMS.PDF) If you can't find a copy, email me and +I'll point you towards it. Feel free to contact me if you have a question. + +Frank Palazzolo + +palazzol@tir.com + + + + +Downloaded from: http://files.emuwiki.com/tms5220.7z diff --git a/tms5220~/tms5220/tms5220r.c b/tms5220~/tms5220/tms5220r.c new file mode 100644 index 0000000..93ba249 --- /dev/null +++ b/tms5220~/tms5220/tms5220r.c @@ -0,0 +1,105 @@ +/* TMS5220 ROM Tables */ + +/* This is the energy lookup table (4-bits -> 10-bits) */ + +const unsigned short energytable[0x10]={ +0x0000,0x00C0,0x0140,0x01C0,0x0280,0x0380,0x0500,0x0740, +0x0A00,0x0E40,0x1440,0x1C80,0x2840,0x38C0,0x5040,0x7FC0}; + +/* This is the pitch lookup table (6-bits -> 8-bits) */ + +const unsigned short pitchtable [0x40]={ +0x0000,0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600, +0x1700,0x1800,0x1900,0x1A00,0x1B00,0x1C00,0x1D00,0x1E00, +0x1F00,0x2000,0x2100,0x2200,0x2300,0x2400,0x2500,0x2600, +0x2700,0x2800,0x2900,0x2A00,0x2B00,0x2D00,0x2F00,0x3100, +0x3300,0x3500,0x3600,0x3900,0x3B00,0x3D00,0x3F00,0x4200, +0x4500,0x4700,0x4900,0x4D00,0x4F00,0x5100,0x5500,0x5700, +0x5C00,0x5F00,0x6300,0x6600,0x6A00,0x6E00,0x7300,0x7700, +0x7B00,0x8000,0x8500,0x8A00,0x8F00,0x9500,0x9A00,0xA000}; + +/* These are the reflection coefficient lookup tables */ + +/* K1 is (5-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k1table [0x20]={ +0x82C0,0x8380,0x83C0,0x8440,0x84C0,0x8540,0x8600,0x8780, +0x8880,0x8980,0x8AC0,0x8C00,0x8D40,0x8F00,0x90C0,0x92C0, +0x9900,0xA140,0xAB80,0xB840,0xC740,0xD8C0,0xEBC0,0x0000, +0x1440,0x2740,0x38C0,0x47C0,0x5480,0x5EC0,0x6700,0x6D40}; + +/* K2 is (5-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k2table [0x20]={ +0xAE00,0xB480,0xBB80,0xC340,0xCB80,0xD440,0xDDC0,0xE780, +0xF180,0xFBC0,0x0600,0x1040,0x1A40,0x2400,0x2D40,0x3600, +0x3E40,0x45C0,0x4CC0,0x5300,0x5880,0x5DC0,0x6240,0x6640, +0x69C0,0x6CC0,0x6F80,0x71C0,0x73C0,0x7580,0x7700,0x7E80}; + +/* K3 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k3table [0x10]={ +0x9200,0x9F00,0xAD00,0xBA00,0xC800,0xD500,0xE300,0xF000, +0xFE00,0x0B00,0x1900,0x2600,0x3400,0x4100,0x4F00,0x5C00}; + +/* K4 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k4table [0x10]={ +0xAE00,0xBC00,0xCA00,0xD800,0xE600,0xF400,0x0100,0x0F00, +0x1D00,0x2B00,0x3900,0x4700,0x5500,0x6300,0x7100,0x7E00}; + +/* K5 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k5table [0x10]={ +0xAE00,0xBA00,0xC500,0xD100,0xDD00,0xE800,0xF400,0xFF00, +0x0B00,0x1700,0x2200,0x2E00,0x3900,0x4500,0x5100,0x5C00}; + +/* K6 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k6table [0x10]={ +0xC000,0xCB00,0xD600,0xE100,0xEC00,0xF700,0x0300,0x0E00, +0x1900,0x2400,0x2F00,0x3A00,0x4500,0x5000,0x5B00,0x6600}; + +/* K7 is (4-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k7table [0x10]={ +0xB300,0xBF00,0xCB00,0xD700,0xE300,0xEF00,0xFB00,0x0700, +0x1300,0x1F00,0x2B00,0x3700,0x4300,0x4F00,0x5A00,0x6600}; + +/* K8 is (3-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k8table [0x08]={ +0xC000,0xD800,0xF000,0x0700,0x1F00,0x3700,0x4F00,0x6600}; + +/* K9 is (3-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k9table [0x08]={ +0xC000,0xD400,0xE800,0xFC00,0x1000,0x2500,0x3900,0x4D00}; + +/* K10 is (3-bits -> 9 bits+sign, 2's comp. fractional (-1 < x < 1) */ + +const short k10table [0x08]={ +0xCD00,0xDF00,0xF100,0x0400,0x1600,0x2000,0x3B00,0x4D00}; + +/* chirp table */ + +static char chirptable[41]={ +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 +}; + +/* interpolation coefficients */ + +static char interp_coeff[8] = { +8, 8, 8, 4, 4, 2, 2, 1 +}; + diff --git a/tms5220~/tms5220~-help.pd b/tms5220~/tms5220~-help.pd new file mode 100644 index 0000000..c01898c --- /dev/null +++ b/tms5220~/tms5220~-help.pd @@ -0,0 +1,30 @@ +#N canvas 1 25 574 513 10; +#X obj 108 206 tms5220~; +#X floatatom 123 285 5 0 0 0 - - -; +#X floatatom 138 262 5 0 0 0 - - -; +#X floatatom 153 239 5 0 0 0 - - -; +#X obj 108 390 dac~; +#X obj 175 343 hsl 128 15 0 1 0 0 empty empty OUTPUT_LEVEL -2 -8 0 +10 -262144 -1 -1 0 1; +#X obj 108 342 *~ 0; +#X msg 172 383 \; pd dsp 1; +#X text 204 238 <-- interrupt; +#X text 202 260 <-- ready; +#X text 201 283 <-- status bits; +#X msg 108 127 write \$1; +#X msg 135 166 reset; +#X text 43 443 (C) Federico Ferri - 2010; +#X msg 108 97 11 \, 20 \, 34 \, 128 \, 255; +#X text 275 94 <-- send sequences of data using write method; +#X text 294 115 (check the TMS5220 datasheet); +#X connect 0 0 6 0; +#X connect 0 1 1 0; +#X connect 0 2 2 0; +#X connect 0 3 3 0; +#X connect 5 0 6 1; +#X connect 5 0 7 0; +#X connect 6 0 4 0; +#X connect 6 0 4 1; +#X connect 11 0 0 0; +#X connect 12 0 0 0; +#X connect 14 0 11 0; diff --git a/tms5220~/tms5220~-meta.pd b/tms5220~/tms5220~-meta.pd new file mode 100644 index 0000000..38451af --- /dev/null +++ b/tms5220~/tms5220~-meta.pd @@ -0,0 +1,8 @@ +#N canvas 10 10 200 200 10; +#N canvas 20 20 420 300 META 0; +#X text 10 10 META this is a prototype of a libdir meta file; +#X text 10 30 NAME tms5220~; +#X text 10 50 AUTHOR Federico Ferri; +#X text 10 70 LICENSE GPL; +#X text 10 90 VERSION 0.1; +#X restore 10 10 pd META; diff --git a/tms5220~/tms5220~.c b/tms5220~/tms5220~.c new file mode 100644 index 0000000..ce119cd --- /dev/null +++ b/tms5220~/tms5220~.c @@ -0,0 +1,120 @@ +/* (C) 2010 Federico Ferri + * this software is gpl'ed software, read the file "README.txt" for details + */ + +#include "tms5220/tms5220.c" + +#include "m_pd.h" + +static t_class *tms5220_tilde_class; + +typedef struct _tms5220_tilde { + t_object x_obj; + + // status outlets + t_int status; + t_outlet *x_status; + t_int ready; + t_outlet *x_ready; + t_int interrupt; + t_outlet *x_interrupt; + + t_float dummy; +} t_tms5220_tilde; + +void tms5220_tilde_reset(t_tms5220_tilde *x) { + tms5220_reset(); +} + +void *tms5220_tilde_new(t_symbol *s, int argc, t_atom *argv) { + t_tms5220_tilde *x = (t_tms5220_tilde *)pd_new(tms5220_tilde_class); + + x->status = x->ready = x->interrupt = 0; + + //inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + //floatinlet_new(&x->x_obj, &x->f_x); + + outlet_new(&x->x_obj, &s_signal); + x->x_status = outlet_new(&x->x_obj, &s_float); + x->x_ready = outlet_new(&x->x_obj, &s_float); + x->x_interrupt = outlet_new(&x->x_obj, &s_float); + + tms5220_tilde_reset(x); + + return (void *)x; +} + +void tms5220_tilde_free(t_tms5220_tilde *x) { +} + +void tms5220_tilde_write(t_tms5220_tilde *x, t_floatarg data) { + tms5220_data_write((int)data); +} + +void tms5220_tilde_update_status(t_tms5220_tilde *x) { + t_int new_status, new_ready, new_interrupt; + + new_status = tms5220_status_read(); + new_ready = tms5220_ready_read(); + new_interrupt = tms5220_int_read(); + + if(new_interrupt != x->interrupt) { + outlet_float(x->x_interrupt, new_interrupt); + x->interrupt = new_interrupt; + } + + if(new_ready != x->ready) { + outlet_float(x->x_ready, new_ready); + x->ready = new_ready; + } + + if(new_status != x->status) { + outlet_float(x->x_status, new_status); + x->status = new_status; + } +} + +t_int *tms5220_tilde_perform(t_int *w) { + t_tms5220_tilde *x = (t_tms5220_tilde *)(w[1]); + t_sample *in = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + + unsigned char *bytebuf = (unsigned char *)malloc(sizeof(unsigned char)*n); + + if(!bytebuf) {error("FATAL: cannot allocate signal buffer"); return w;} + + tms5220_process(bytebuf, n); + unsigned char *pb = bytebuf; + + while (n--) *out++ = (0.5+((t_sample)*pb++))/127.5; + + free(bytebuf); + + tms5220_tilde_update_status(x); + + return (w+5); +} + +void tms5220_tilde_dsp(t_tms5220_tilde *x, t_signal **sp) { + dsp_add(tms5220_tilde_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void tms5220_tilde_setup(void) { + tms5220_tilde_class = class_new(gensym("tms5220~"), + (t_newmethod)tms5220_tilde_new, + (t_method)tms5220_tilde_free, + sizeof(t_tms5220_tilde), + CLASS_DEFAULT, A_GIMME, 0); + + CLASS_MAINSIGNALIN(tms5220_tilde_class, t_tms5220_tilde, dummy); + + class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_dsp, gensym("dsp"), 0); + //class_addfloat(tms5220_tilde_class, (t_method)tms5220_tilde_write); + class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_write, gensym("write"), A_DEFFLOAT, 0); + class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_reset, gensym("reset"), 0); + + post("tms5220~: TSM5220 IC emulation"); + post("tms5220~: external by Federico Ferri "); + post("tms5220~: based on code by Frank Palazzolo "); +} -- cgit v1.2.1