rendered paste body/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id:$ * * Copyright (C) 2006 Michael Sevakis * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/#include "plugin.h"/* Button mapping */#if CONFIG_KEYPAD == IRIVER_H100_PAD#define EFFECT_QUIT BUTTON_ON#elif CONFIG_KEYPAD == IAUDIO_X5_PAD#define EFFECT_QUIT BUTTON_POWER#endifPLUGIN_HEADERPLUGIN_IRAM_DECLAREstatic struct plugin_api* rb;static int queue_size;/* Imporant to leave enough room behind effect_index to allow for delays like screen updates */static volatile int wr_index = 4;static volatile int rd_index = 0;static int effect_index = 4; /* one ahead to account for preincrement of wr_index */static unsigned long effect_thread_stack[DEFAULT_STACK_SIZE] IBSS_ATTR;static bool stop_effect = false;#define CHUNK_SAMPLES 128#define CHUNK_SIZE (CHUNK_SAMPLES*4)#define NUM_CHUNKS 8 /* power of 2 */#define CHUNK_MASK (NUM_CHUNKS-1)#define NEXT_CHUNK(x) (((x) + 1) & CHUNK_MASK)typedef int32_t (* buffer_t)[CHUNK_SAMPLES];buffer_t __attribute__((aligned(16))) queue;/* Effect data */struct lfo { int32_t phase, delta;};struct delay { size_t writepos; size_t mask; int32_t delay; int16_t *delayline;};struct lfo trilfo = { 0, 0xffffffff/44100/5};int16_t delayline_l[65536], delayline_r[65536];struct delay delay1 = { 0, 0xffff, (10000 << 15) + 0x1fff, delayline_l };static inline int32_t saw_lfo(struct lfo *l){ int32_t ret = l->phase; l->phase += l->delta; return ret;}static inline int32_t tri_lfo(struct lfo *l){ int32_t ret = (abs(l->phase + 0x3fffffff) - 0x3fffffff) << 1; l->phase += l->delta; return ret;}static inline int32_t par_lfo(struct lfo *l){ int32_t shift_phase = l->phase >> 16; int32_t ret = 8*shift_phase*((1 << 15) - abs(shift_phase)); l->phase += l->delta; return ret;}static void delay_process(int16_t *data, struct delay *d, int num) ICODE_ATTR;static void delay_process(int16_t *data, struct delay *d, int num){ int i; size_t mask = d->mask; size_t writepos = d->writepos; for (i = 0; i < num; ++i) { d->delay = (441 << 15) + par_lfo(&trilfo)/1600; size_t r = writepos - (d->delay >> 15); int32_t frac = 0x8000 - (d->delay & 0x7fff); const int32_t xm1 = d->delayline[(r - 1) & mask]; const int32_t x0 = d->delayline[r & mask]; const int32_t xp1 = d->delayline[(r + 1) & mask]; const int32_t xp2 = d->delayline[(r + 2) & mask]; const int32_t c = (xp1 - xm1)/2; const int32_t v = x0 - xp1; const int32_t w = c + v; const int32_t a = w + v + (xp2 - x0)/2; const int32_t b = w + a; int32_t output = (((((a*frac >> 15) - b)*frac >> 15) + c)*frac >> 15) + x0; int32_t input = *data; d->delayline[writepos] = input + output*2/10; *data++ = (input + output)/2; *data++ = (input + output)/2; writepos = (writepos + 1) & mask; } d->writepos = writepos;}static void play_callback(unsigned char **start, size_t *size) ICODE_ATTR;static void play_callback(unsigned char **start, size_t *size){ rd_index = NEXT_CHUNK(rd_index); *start = (unsigned char *)queue[rd_index]; *size = CHUNK_SIZE;}static int record_callback(int status) ICODE_ATTR;static int record_callback(int status){ wr_index = NEXT_CHUNK(wr_index); rb->pcm_record_more(queue[wr_index], CHUNK_SIZE); return 0; (void)status;}static void effect_thread(void) ICODE_ATTR;static void effect_thread(void){ int y = 0; while (!stop_effect) { /* Wait for at least one chunk of data available */ while (wr_index == effect_index) { rb->yield(); if (stop_effect) goto effect_done; } do { int16_t *ptr = (int16_t *)queue[effect_index]; delay_process(ptr, &delay1, CHUNK_SAMPLES); effect_index = NEXT_CHUNK(effect_index); if (++y >= 8) { rb->yield(); y = 0; } } while (effect_index != wr_index); }effect_done: /* main thread waits for this to turn false again */ stop_effect = false; rb->remove_thread(NULL);}/* this is the plugin entry point */enum plugin_status plugin_start(struct plugin_api* api, void* parameter){ PLUGIN_IRAM_INIT(api); rb = api; queue = (buffer_t)rb->plugin_get_audio_buffer(&queue_size); rb->memset(queue, 0, CHUNK_SIZE*NUM_CHUNKS); rb->memset(delayline_l, 0, sizeof(delayline_l)); rb->pcm_init_recording(); rb->pcm_set_frequency(SAMPR_44); rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); rb->rec_set_source(AUDIO_SRC_LINEIN, SRCF_RECORDING); rb->pcm_apply_settings(true); rb->cpu_boost(true); rb->pcm_record_data(record_callback, queue[wr_index], CHUNK_SIZE); rb->pcm_play_data(play_callback, NULL, 0); rb->sound_set(SOUND_VOLUME, rb->sound_default(SOUND_VOLUME)); rb->audio_set_recording_gain(rb->sound_default(SOUND_LEFT_GAIN) + 10, rb->sound_default(SOUND_RIGHT_GAIN) + 10, AUDIO_GAIN_LINEIN); /* Start a thread for processing the audio */ rb->create_thread(effect_thread, effect_thread_stack, sizeof(effect_thread_stack), "effect thread", PRIORITY_RECORDING); while (rb->button_get_w_tmo(HZ/10) != EFFECT_QUIT) { char buf[32]; rb->snprintf(buf, ARRAYLEN(buf), "rd: %d ef:%d wr: %d", rd_index, effect_index, wr_index); rb->lcd_puts(0, 0, buf); rb->yield(); rb->lcd_update(); rb->yield(); } /* stop effect thread - it will remove itself */ stop_effect = true; while (stop_effect) rb->yield(); rb->pcm_play_stop(); rb->pcm_stop_recording(); rb->pcm_close_recording(); rb->cpu_boost(false); rb->audio_set_output_source(AUDIO_SRC_PLAYBACK); rb->pcm_set_frequency(HW_SAMPR_DEFAULT); rb->rec_set_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); rb->pcm_apply_settings(true); return PLUGIN_OK; (void)parameter;}