Index: apps/dsp.c =================================================================== --- apps/dsp.c (revision 21259) +++ apps/dsp.c (working copy) @@ -34,6 +34,10 @@ #include "debug.h" #include "tdspeed.h" #include "buffer.h" +#include "file.h" +#include "sprintf.h" +#include "strcat.c" +#include "strcpy.c" /* 16-bit samples are scaled based on these constants. The shift should be * no more than 15. @@ -45,6 +49,7 @@ /* If the small buffer size changes, check the assembly code! */ #define SMALL_SAMPLE_BUF_COUNT 256 #define DEFAULT_GAIN 0x01000000 +#define MAX_WAVS 50 /* max number of wav files created */ /* enums to index conversion properly with stereo mode and other settings */ enum @@ -219,6 +224,32 @@ static long replaygain; static bool crossfeed_enabled; +/* wav write */ + bool wav_enabled; + bool wav_open; +static int fd; +static uint32_t datasize; +static const uint8_t wav_header[44] = { + 'R', 'I', 'F', 'F', /* chunk ID */ + 0, 0, 0, 0, /* chunk size - to be calculated later */ + 'W', 'A', 'V', 'E', /* format */ + 'f', 'm', 't', ' ', /* subchunk1 ID */ + 16, 0, 0, 0, /* subchunk1 size */ + 1, 0, /* format = PCM */ + 2, 0, /* num channels = 2 */ + 0x44, 0xAC, 0, 0, /* sample rate = 44100 */ + 0x10, 0xB1, 0x02, 0, /* byte rate = 176400 */ + 4, 0, /* block align = 4 bytes per sample */ + 16, 0, /* bits per sample = 16 */ + 'd', 'a', 't', 'a', /* subchunk2 ID */ + 0, 0, 0, 0 /* subchunk2 size - to be calculated later */ + }; + +static void wavopen(void); +static void wavwrite(int count, int16_t *buf); +static uint8_t *force_little_endian(uint32_t value, uint8_t *buf); +static bool write_to_header(uint32_t value, off_t pos); + #define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO]) #define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE]) @@ -1247,6 +1278,13 @@ dsp->channels_process(chunk, t2); dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst); + + if (wav_enabled && !wav_open) + wavopen(); + if (wav_open) + wavwrite (chunk, (int16_t *)dst); + + /* DEBUGF("%d\t", chunk); */ written += chunk; dst += chunk * sizeof (int16_t) * 2; @@ -1515,3 +1554,136 @@ replaygain = gain; set_gain(&AUDIO_DSP); } + +/* When enabled in the debug menu, a wav file, starting with "TEST0001.wav", + * is created in the root directory whenever playback is started. The + * contents of the wav file are the same as the data placed in the PCM + * buffer. When playback is stopped, the file is closed. Previously + * created wav files are not overwritten. Instead the file name is + * incremented to the next available file name. The maximum number of wav + * files is set with the MAX_WAVS define statement. */ + +static void wavopen(void) +{ + + char filename[14]; + char filenum[5]; + int i, errorcode; + ssize_t bytes_written; + + if (!wav_enabled) return; + wav_enabled = false; + for (i = 1; i <= MAX_WAVS; i++) /* find a free file name */ + { + strcpy(filename, "/test"); + snprintf(filenum, 5, "%04d", i); + strcat(strcat(filename, filenum), ".wav"); + fd = open(filename, O_RDONLY); + if (fd == -1) break; /* found one */ + if ((fd < 0) || (close(fd) != 0)) return; /* some error */ + } + + if (fd >= 0) return; /* no free file names */ + + /* create the wav file */ + datasize = 0; + fd = creat(filename); + DEBUGF("wav open: %s\n", filename); + if ((wav_open = (fd >= 0))) + { + /* write .wav header */ + bytes_written = write(fd, wav_header, 44); + if (bytes_written < 0) + { + DEBUGF("Error writing to wav file: %ld\n", (long)bytes_written); + errorcode = close(fd); + DEBUGF("wav close\n"); + if (errorcode < 0) + DEBUGF ("Error closing wav file: %i\n", (int)errorcode); + wav_open = false; + return; + } + wav_enabled = true; + } + else DEBUGF("Error opening wav file: %i\n", fd); +} + +static void wavwrite(int count, int16_t *buf) +{ + uint16_t lsamp, rsamp; + uint8_t sample[4]; + ssize_t bytes_written; + + if(!wav_open) return; + do + { + /* convert signed to unsigned */ + lsamp = 0x10000 + *buf++; + rsamp = 0x10000 + *buf++; + + /* wav files are little endian and interlaced */ + bytes_written = write(fd, \ + force_little_endian(((rsamp << 16) | lsamp), sample), 4); + if (bytes_written < 0) + { + if (bytes_written <= -2) + datasize += (-(bytes_written + 2) / 10); + DEBUGF("Error writing to wav file: %ld\n", (long)bytes_written); + wav_enabled = false; + wavclose(); + } + datasize += bytes_written; + } + while (--count > 0); +} + +void wavclose(void) +{ + if(wav_open) + { + int errorcode; + + wav_enabled = (write_to_header(datasize + 36, 4) && + write_to_header(datasize, 40)); + errorcode = close(fd); + DEBUGF("wav close: %ld data bytes\n", datasize); + if (errorcode < 0) + { + DEBUGF ("Error closing wav file: %i\n", errorcode); + wav_enabled = false; + } + wav_open = false; + } +} + +static uint8_t *force_little_endian(uint32_t value, uint8_t *buf) +{ + int i; + + for (i=0; i<4; i++) + { + buf[i] = (value & 0xFF); + value >>= 8; + } + return buf; +} + +static bool write_to_header(uint32_t value, off_t pos) +{ + off_t seek_return; + uint8_t header[4]; + ssize_t bytes_written; + bool ok = true; + + seek_return = lseek(fd, pos, SEEK_SET); + if (seek_return < 0) + DEBUGF("Error seeking header: %ld\n", (long)seek_return); + bytes_written = write(fd, force_little_endian(value, header), 4); + if (bytes_written < 0) + { + DEBUGF("Error writing to header: %ld : %ld\n", \ + (long)pos, (long)bytes_written); + ok = false; + } + return ok; +} Index: apps/dsp.h =================================================================== --- apps/dsp.h (revision 21259) +++ apps/dsp.h (working copy) @@ -168,5 +168,6 @@ void dsp_set_timestretch(int percent); bool dsp_timestretch_enabled(void); int dsp_get_timestretch(void); +void wavclose(void); #endif Index: apps/playback.c =================================================================== --- apps/playback.c (revision 21259) +++ apps/playback.c (working copy) @@ -2179,6 +2179,8 @@ static void audio_stop_playback(void) { + extern bool wav_open; + /* If we were playing, save resume information */ if (playing) { @@ -2214,6 +2216,9 @@ /* Close all tracks */ audio_release_tracks(); + + if (wav_open) + wavclose(); } static void audio_play_start(size_t offset) Index: apps/debug_menu.c =================================================================== --- apps/debug_menu.c (revision 21259) +++ apps/debug_menu.c (working copy) @@ -404,6 +404,18 @@ return false; } + +/* wav write */ +extern bool wav_enabled; + +static bool dbg_wavout(void) +{ + wav_enabled = !wav_enabled; + splashf(HZ, "WAV output %s", + wav_enabled?"enabled":"disabled"); + return false; +} + #endif /* CONFIG_CODEC */ #endif /* HAVE_LCD_BITMAP */ @@ -2763,6 +2775,7 @@ #ifdef HAVE_LCD_BITMAP #if CONFIG_CODEC == SWCODEC { "View buffering thread", dbg_buffering_thread }, + { "WAV output", dbg_wavout }, #elif !defined(SIMULATOR) { "View audio thread", dbg_audio_thread }, #endif