Index: apps/metadata/tap.c =================================================================== --- apps/metadata/tap.c (Revision 0) +++ apps/metadata/tap.c (Revision 0) @@ -0,0 +1,33 @@ +#include +#include +#include +#include +#include + +#include "system.h" +#include "metadata.h" +#include "metadata_common.h" +#include "metadata_parsers.h" +#include "rbunicode.h" + +#define AVERAGE_ZX_LOADING_BPS 1365 + +static bool parse_tap_header(int fd, struct mp3entry* id3) +{ + id3->filesize = filesize(fd); + + id3->length = ((id3->filesize<<3) / AVERAGE_ZX_LOADING_BPS)*1000LL; + + return true; +} + +bool get_tap_metadata(int fd, struct mp3entry* id3) +{ + id3->vbr = false; + + /* we only render 16 bits, 44.1KHz, Mono */ + id3->bitrate = 706; + id3->frequency = 44100; + + return parse_tap_header(fd, id3); +} Index: apps/metadata/metadata_parsers.h =================================================================== --- apps/metadata/metadata_parsers.h (Revision 31194) +++ apps/metadata/metadata_parsers.h (Arbeitskopie) @@ -53,3 +53,4 @@ bool get_sgc_metadata(int fd, struct mp3entry* id3); bool get_vgm_metadata(int fd, struct mp3entry* id3); bool get_kss_metadata(int fd, struct mp3entry* id3); +bool get_tap_metadata(int fd, struct mp3entry* id3); Index: apps/metadata.c =================================================================== --- apps/metadata.c (Revision 31194) +++ apps/metadata.c (Arbeitskopie) @@ -222,7 +222,7 @@ /* GBS (Game Boy Sound Format) */ [AFMT_GBS] = AFMT_ENTRY("GBS", "gbs", NULL, get_gbs_metadata, "gbs\0"), - /* HES (Hudson Entertainment System Sound Format) */ + /* HES (Hudson Entertainment System Sound Format) */ [AFMT_HES] = AFMT_ENTRY("HES", "hes", NULL, get_hes_metadata, "hes\0"), /* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */ @@ -234,6 +234,9 @@ /* KSS (MSX computer KSS Music File) */ [AFMT_KSS] = AFMT_ENTRY("KSS", "kss", NULL, get_kss_metadata, "kss\0"), + /* TAP (ZX Spectrum Tape Format) */ + [AFMT_TAP] = + AFMT_ENTRY("TAP", "tap", NULL, get_tap_metadata, "tap\0"), #endif }; @@ -323,6 +326,7 @@ case AFMT_SGC: case AFMT_VGM: case AFMT_KSS: + case AFMT_TAP: /* Type must be allocated and loaded in its entirety onto the buffer */ return TYPE_ATOMIC_AUDIO; Index: apps/metadata.h =================================================================== --- apps/metadata.h (Revision 31194) +++ apps/metadata.h (Arbeitskopie) @@ -86,12 +86,13 @@ AFMT_WMAVOICE, /* WMA Voice in ASF */ AFMT_MPC_SV8, /* Musepack SV8 */ AFMT_MP4_AAC_HE, /* Advanced Audio Coding (AAC-HE) in M4A container */ - AFMT_AY, /* AY (ZX Spectrum, Amstrad CPC Sound Format) */ + AFMT_AY, /* AY (ZX Spectrum, Amstrad CPC Sound Format) */ AFMT_GBS, /* GBS (Game Boy Sound Format) */ AFMT_HES, /* HES (Hudson Entertainment System Sound Format) */ AFMT_SGC, /* SGC (Sega Master System, Game Gear, Coleco Vision Sound Format) */ - AFMT_VGM, /* VGM (Video Game Music Format) */ + AFMT_VGM, /* VGM (Video Game Music Format) */ AFMT_KSS, /* KSS (MSX computer KSS Music File) */ + AFMT_TAP, /* TAP (ZX Spectrum Tape file format) */ #endif /* add new formats at any index above this line to have a sensible order - Index: apps/SOURCES =================================================================== --- apps/SOURCES (Revision 31194) +++ apps/SOURCES (Arbeitskopie) @@ -232,6 +232,7 @@ metadata/sgc.c metadata/vgm.c metadata/kss.c +metadata/tap.c #endif #ifdef HAVE_TAGCACHE tagcache.c Index: apps/filetypes.c =================================================================== --- apps/filetypes.c (Revision 31194) +++ apps/filetypes.c (Arbeitskopie) @@ -119,6 +119,7 @@ { "vgm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "vgz", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, { "kss", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, + { "tap", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA }, #endif { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST }, Index: apps/codecs/SOURCES =================================================================== --- apps/codecs/SOURCES (Revision 31194) +++ apps/codecs/SOURCES (Arbeitskopie) @@ -38,6 +38,8 @@ nsf.c sgc.c vgm.c +tap.c + #if MEMORYSIZE > 2 kss.c #endif Index: apps/codecs/tap.c =================================================================== --- apps/codecs/tap.c (Revision 0) +++ apps/codecs/tap.c (Revision 0) @@ -0,0 +1,312 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: tap.c 2011-12-08 07:45:35 $ + * + * Copyright (C) 2011 George Manolaros + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + +#include + +CODEC_HEADER + +#define INT16_T_MAX 32767 + +#define CHUNK_SAMPLES 1024 +#define CHUNK_SIZE (CHUNK_SAMPLES*2) +#define M_PI 3.141592653589 + +/* http://www.zxshed.co.uk/sinclairfaq/index.php5?title=ZX_Spectrum_Tape_Format */ + +struct signal { + uint16_t size; + int16_t samples[64]; /* be careful with this, .size should be <=64 */ +}; + +static int16_t samples[CHUNK_SIZE] IBSS_ATTR; + +static int buf_pos = 0; +static int file_pos = 0; + +static struct signal signal_bit_zero = {.size=11+11}; +static struct signal signal_bit_one = {.size=22+22}; + +static struct signal signal_pilot = {.size=27+27}; +static struct signal signal_synch = {.size=8+9}; + +/****************** rockbox interface ******************/ + +static float sine(float x) +{ + /* sin approximation, found it on the web : */ + /* http://lab.polygonal.de/2007/07/18/fast-and-accurate-sinecosine-approximation */ + + /* always wrap input angle to -PI..PI */ + if (x < -M_PI) + x += 2*M_PI; + else if (x > M_PI) + x -= 2*M_PI; + + float sin = 0; + + /* compute sine */ + if (x < 0) + { + sin = 1.27323954 * x + .405284735 * x * x; + + if (sin < 0) + sin = .225 * (sin *-sin - sin) + sin; + else + sin = .225 * (sin * sin - sin) + sin; + } + else + { + sin = 1.27323954 * x - 0.405284735 * x * x; + + if (sin < 0) + sin = .225 * (sin *-sin - sin) + sin; + else + sin = .225 * (sin * sin - sin) + sin; + } + + return sin; +} + +static void create_pulse(bool bit, int duration, int16_t* buffer) +{ + float p = 0.0f; + if (bit==false) p = M_PI; + + for (int i=0;ipcmbuf_insert(samples, NULL, CHUNK_SAMPLES); + ci->set_elapsed((ci->id3->length * file_pos) / ci->filesize); +} + +static inline void play_signal(struct signal* signal) +{ + for (int i=0;isize;i++) + { + samples[buf_pos] = signal->samples[i]; + buf_pos++; + if (buf_pos == CHUNK_SAMPLES) + play_buffer(); + } +} + + +static inline void play_bit(uint8_t bit) +{ + if (bit==0) + { + play_signal(&signal_bit_zero); + } + else + { + play_signal(&signal_bit_one); + } +} + +static inline void play_byte(uint8_t byte) +{ + play_bit( (byte & 128) >> 7); + play_bit( (byte & 64) >> 6); + play_bit( (byte & 32) >> 5); + play_bit( (byte & 16) >> 4); + play_bit( (byte & 8) >> 3); + play_bit( (byte & 4) >> 2); + play_bit( (byte & 2) >> 1); + play_bit( (byte & 1) >> 0); +} + +static inline void play_pilot(int duration) +{ + /* PILOT: 8063 (header) or 3223 (data) alternately high and low pulses, each 2168 ticks long */ + + DEBUGF("TAP: play_pilot(%d)\n", duration); + + for (int i=0;iconfigure(DSP_SET_SAMPLE_DEPTH, 16); + + /* 44 Khz, Interleaved stereo */ + ci->configure(DSP_SET_FREQUENCY, 44100); + ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); + + /* A '0' bit is encoded as 2 pulses of 855 T-states each. */ + create_pulse(true, 11, &signal_bit_zero.samples[0]); /* 855 T-states = 10.773 samples */ + create_pulse(false, 11, &signal_bit_zero.samples[11]); + + /* A '1' bit is encoded as 2 pulses of 1710 T-states each */ + create_pulse(true, 22, &signal_bit_one.samples[0]); /* 1710 T-states = 21.546 samples */ + create_pulse(false, 22, &signal_bit_one.samples[22]); + + /* PILOT: Alternately high and low pulses, each 2168 ticks long */ + create_pulse(true, 27, &signal_pilot.samples[0]); /* 2168 T-states = 27.3168 samples */ + create_pulse(false, 27, &signal_pilot.samples[27]); /* 2168 T-states = 27.3168 samples */ + + /* SYNC: A high pulse 667 ticks long, followed by a low pulse 735 ticks long */ + create_pulse(true, 8, &signal_synch.samples[0]); /* 667 T-states = 8.4042 samples */ + create_pulse(false, 9, &signal_synch.samples[8]); /* 735 T-states = 9.261 samples */ + + DEBUGF("TAP: codec_main::CODEC_OK\n"); + + return CODEC_OK; /* OR return CODEC_ERROR if init fails */ + + } + else if (reason == CODEC_UNLOAD) + { + DEBUGF("TAP: codec_main::CODEC_UNLOAD\n"); + + /* Codec is about to be removed from memory; clean up any resouces + allocated */ + DEBUGF("TAP: codec_main::CODEC_OK\n"); + return CODEC_OK; /* Return value ignored */ + } + else + { + DEBUGF("TAP: codec_main::CODEC_OTHER\n"); + /* Default response in cases where codec has nothing to do when + codec_main is called for a particular reason */ + return CODEC_OK; + } +} + +/* this is called for each file to process */ +enum codec_status codec_run(void) +{ + uint8_t *buf; + intptr_t param; + size_t n; + + if (codec_init()) { + DEBUGF("TAP: codec init failed\n"); + return CODEC_ERROR; + } + + codec_set_replaygain(ci->id3); + + /* Read the entire file */ + ci->seek_buffer(0); + buf = ci->request_buffer(&n, ci->filesize); + + if (!buf || n < (size_t)ci->filesize) + { + DEBUGF("TAP: file load failed\n"); + return CODEC_ERROR; + } + + DEBUGF("TAP: filesize: %d, read: %d\n", (int) ci->filesize, (int) n); + + ci->set_elapsed(0); + + DEBUGF("TAP: play\n"); + + buf_pos = 0; + file_pos = 0; + bool header = true; + + /* The main decoder loop */ + while (true) + { + enum codec_command_action action = ci->get_command(¶m); + + if (action == CODEC_ACTION_HALT) + break; + + uint16_t hb = (uint16_t) buf[file_pos]; + file_pos++; if (file_pos == (ci->filesize) ) return CODEC_ERROR; + uint16_t hl = (uint16_t) buf[file_pos]; + file_pos++; if (file_pos == (ci->filesize) ) return CODEC_ERROR; + + uint16_t blocksize = ( hb | (hl << 8) ); + + DEBUGF("TAP: blocksize: %d\n", blocksize); + + if (header==true) + { + play_pilot(4032); + play_sync(); + header = false; + } + else + { + play_pilot(1612); + play_sync(); + header=true; + } + + if ((ci->filesize - file_pos) < blocksize ) return CODEC_ERROR; + + DEBUGF("TAP: play_block\n"); + for (int i=0;ifilesize) ) + { + play_pilot(1024); + play_sync(); + + DEBUGF("TAP: completed with no errors\n"); + return CODEC_OK; + } + + } + + return CODEC_OK; +} Index: manual/appendix/wps_tags.tex =================================================================== --- manual/appendix/wps_tags.tex (Revision 31194) +++ manual/appendix/wps_tags.tex (Arbeitskopie) @@ -131,7 +131,7 @@ realaudioaac|realaudioac3|realaudioatrac3|cmc|\newline cm3|cmr|cms|dmc|dlt|mpt|mpd|rmt|tmc|tm8|tm2|\newline omaatrac3|smaf|au|vox|wave64|tta|wmavoice|mpcsv8|\newline - aache|ay|gbs|hes|sgc|vgm|kss|unknown>}. + aache|ay|gbs|hes|sgc|vgm|kss|tap|unknown>}. The codec order is as shown above.\\ \config{\%ff} & File Frequency (in Hz)\\ \config{\%fk} & File Frequency (in kHz)\\ Index: manual/appendix/file_formats.tex =================================================================== --- manual/appendix/file_formats.tex (Revision 31194) +++ manual/appendix/file_formats.tex (Arbeitskopie) @@ -248,6 +248,9 @@ SPC700 & \fname{.spc} & \\ + ZX Spectrum Tape Format + & \fname{.tap} + & \\ \end{rbtabular} \note{NSF and VGM might not play in realtime on all devices due to CPU @@ -291,6 +294,7 @@ Atari SAP & x & & \\ Sound Interface Device & x & & \\ SPC700 & x & & \\ + ZX Spectrum Tape Format & & & \\ \end{rbtabular} \note{The seek implementations of NES Sound Format, Sound Interface Device, @@ -324,7 +328,7 @@ \fname{.snd}, \fname{.shn}, \fname{.vox}, \fname{.w64}, \fname{.wav}, \fname{.cmc}, \fname{.cm3}, \fname{.cmr}, \fname{.cms}, \fname{.dmc}, \fname{.dlt}, \fname{.mpt}, - \fname{.mpd}, \fname{.hes}, \fname{.vgz} \\ + \fname{.mpd}, \fname{.hes}, \fname{.vgz}, \fname{.tap} \\ \end{rbtabular} \subsection{Featureset for generic metadata tags}