Index: apps/dsp.c =================================================================== --- apps/dsp.c (revision 21267) +++ apps/dsp.c (working copy) @@ -219,6 +219,36 @@ static long replaygain; static bool crossfeed_enabled; +/* wav write */ +#ifdef WAVOUT +#include "general.h" +#include "file.h" + 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); +#endif + #define AUDIO_DSP (dsp_conf[CODEC_IDX_AUDIO]) #define VOICE_DSP (dsp_conf[CODEC_IDX_VOICE]) @@ -1247,7 +1277,14 @@ dsp->channels_process(chunk, t2); dsp->output_samples(chunk, &dsp->data, (const int32_t **)t2, (int16_t *)dst); - + +#ifdef WAVOUT + if (wav_enabled && !wav_open) + wavopen(); + if (wav_open) + wavwrite(chunk, (int16_t *)dst); +#endif + written += chunk; dst += chunk * sizeof (int16_t) * 2; @@ -1515,3 +1552,122 @@ replaygain = gain; set_gain(&AUDIO_DSP); } + +#ifdef WAVOUT +/* When enabled in the debug menu, a wav file, starting with "TEST_01.wav", + * is created in the root directory whenever playback is started. The + * contents of the wav file are the same as the data output from the DSP + * 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. */ + +static void wavopen(void) +{ + + char filename[MAX_PATH]; + int errorcode; + + if (!wav_enabled) return; + + datasize = 0; + create_numbered_filename(filename, "", "TEST_", ".WAV", 2); + fd = creat(filename); + DEBUGF("wav open: %s\n", filename); + if ((wav_open = (fd >= 0))) + { + errorcode = (int)write(fd, wav_header, 44); + if (errorcode < 0) + { + DEBUGF("Error writing to wav file: %d\n", errorcode); + errorcode = close(fd); + DEBUGF("wav close\n"); + if (errorcode < 0) + DEBUGF ("Error closing wav file: %d\n", errorcode); + wav_open = false; + wav_enabled = false; + return; + } + } + 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(); + return; + } + datasize += bytes_written; + } + while (--count > 0); +} + +void wavclose(void) +{ + int errorcode; + + if(!wav_open) return; + + 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]; + int errorcode; + bool ok = true; + + seek_return = lseek(fd, pos, SEEK_SET); + if (seek_return < 0) + DEBUGF("Error seeking header: %ld\n", (long)seek_return); + errorcode = (int)write(fd, force_little_endian(value, header), 4); + if (errorcode < 0) + { + DEBUGF("Error writing to header at position: %ld, error: %d\n", + (long)pos, errorcode); + ok = false; + } + return ok; +} +#endif /* WAVOUT */ Index: apps/dsp.h =================================================================== --- apps/dsp.h (revision 21267) +++ apps/dsp.h (working copy) @@ -169,4 +169,8 @@ bool dsp_timestretch_enabled(void); int dsp_get_timestretch(void); +#ifdef WAVOUT +void wavclose(void); #endif + +#endif Index: apps/playback.c =================================================================== --- apps/playback.c (revision 21267) +++ apps/playback.c (working copy) @@ -2214,6 +2214,12 @@ /* Close all tracks */ audio_release_tracks(); + +#ifdef WAVOUT + extern bool wav_open; + if (wav_open) + wavclose(); +#endif } static void audio_play_start(size_t offset) Index: apps/debug_menu.c =================================================================== --- apps/debug_menu.c (revision 21267) +++ apps/debug_menu.c (working copy) @@ -404,6 +404,20 @@ return false; } + +#ifdef WAVOUT +/* 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 /* WAVOUT */ + #endif /* CONFIG_CODEC */ #endif /* HAVE_LCD_BITMAP */ @@ -2763,6 +2777,9 @@ #ifdef HAVE_LCD_BITMAP #if CONFIG_CODEC == SWCODEC { "View buffering thread", dbg_buffering_thread }, +#ifdef WAVOUT + { "WAV output", dbg_wavout }, +#endif #elif !defined(SIMULATOR) { "View audio thread", dbg_audio_thread }, #endif Index: firmware/export/config.h =================================================================== --- firmware/export/config.h (revision 21267) +++ firmware/export/config.h (working copy) @@ -806,6 +806,9 @@ #endif /* HAVE_USBSTACK */ +/* allow WAV output only for simulators */ +#ifdef SIMULATOR +#define WAVOUT +#endif - #endif /* __CONFIG_H__ */