diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2008-12-07 00:07:47 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2008-12-07 00:07:47 +0000 |
commit | 89da4328a07c8736c42843607a3f3bf91c17601d (patch) | |
tree | cbd5d6580b2c62dad5830c043d9284ff15cf7bdf | |
parent | 3648e8705402ce5a0af2125987f12c884b540eea (diff) | |
download | rockbox-89da4328a07c8736c42843607a3f3bf91c17601d.tar.gz rockbox-89da4328a07c8736c42843607a3f3bf91c17601d.zip |
Meg F/X can beep and click using a hardware timer so let us try it out. To match things up better, fix PCM beeping to give correct frequency (and get a pointer wrap bug too). Do some minor adjustments to compensate for corrections.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19355 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/action.c | 2 | ||||
-rw-r--r-- | apps/pcmbuf.c | 27 | ||||
-rw-r--r-- | apps/playback.c | 4 | ||||
-rw-r--r-- | firmware/drivers/audio/wm8751.c | 11 | ||||
-rw-r--r-- | firmware/export/config-gigabeat.h | 5 | ||||
-rw-r--r-- | firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c | 12 | ||||
-rw-r--r-- | firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h | 1 | ||||
-rw-r--r-- | firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c | 103 |
8 files changed, 141 insertions, 24 deletions
diff --git a/apps/action.c b/apps/action.c index 5ceeeb896f..5f845ab272 100644 --- a/apps/action.c +++ b/apps/action.c | |||
@@ -130,7 +130,7 @@ static int get_action_worker(int context, int timeout, | |||
130 | /* Produce keyclick */ | 130 | /* Produce keyclick */ |
131 | if (global_settings.keyclick && !(button & BUTTON_REL)) | 131 | if (global_settings.keyclick && !(button & BUTTON_REL)) |
132 | if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats) | 132 | if (!(button & BUTTON_REPEAT) || global_settings.keyclick_repeats) |
133 | pcmbuf_beep(5000, 2, 2500*global_settings.keyclick); | 133 | pcmbuf_beep(4000, 2, 2500*global_settings.keyclick); |
134 | #endif | 134 | #endif |
135 | 135 | ||
136 | if ((context != last_context) && ((last_button & BUTTON_REL) == 0)) | 136 | if ((context != last_context) && ((last_button & BUTTON_REL) == 0)) |
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index 61c6c45de2..c7db4d3101 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c | |||
@@ -954,14 +954,15 @@ bool pcmbuf_insert_buffer(char *buf, int count) | |||
954 | } | 954 | } |
955 | #endif | 955 | #endif |
956 | 956 | ||
957 | #ifndef HAVE_HARDWARE_BEEP | ||
957 | /* Generates a constant square wave sound with a given frequency | 958 | /* Generates a constant square wave sound with a given frequency |
958 | in Hertz for a duration in milliseconds. */ | 959 | in Hertz for a duration in milliseconds. */ |
959 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) | 960 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) |
960 | { | 961 | { |
961 | unsigned int count = 0; | 962 | int i; |
962 | unsigned int i; | 963 | unsigned int step = 0xffffffffu / NATIVE_FREQUENCY * frequency; |
963 | unsigned int interval = NATIVE_FREQUENCY / frequency; | 964 | int32_t phase = 0; |
964 | unsigned int samples = NATIVE_FREQUENCY / 1000 * duration; | 965 | int samples = NATIVE_FREQUENCY / 1000 * duration; |
965 | int32_t sample; | 966 | int32_t sample; |
966 | int16_t *bufstart; | 967 | int16_t *bufstart; |
967 | int16_t *bufptr; | 968 | int16_t *bufptr; |
@@ -986,21 +987,17 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) | |||
986 | bufptr = bufstart; | 987 | bufptr = bufstart; |
987 | for (i = 0; i < samples; ++i) | 988 | for (i = 0; i < samples; ++i) |
988 | { | 989 | { |
990 | int32_t amp = (phase >> 31) ^ (int32_t)amplitude; | ||
989 | sample = mix ? *bufptr : 0; | 991 | sample = mix ? *bufptr : 0; |
990 | *bufptr++ = clip_sample_16(sample + amplitude); | 992 | *bufptr++ = clip_sample_16(sample + amp); |
991 | if (bufptr > pcmbuf_end) | 993 | if (bufptr >= pcmbuf_end) |
992 | bufptr = (int16_t *)audiobuffer; | 994 | bufptr = (int16_t *)audiobuffer; |
993 | sample = mix ? *bufptr : 0; | 995 | sample = mix ? *bufptr : 0; |
994 | *bufptr++ = clip_sample_16(sample + amplitude); | 996 | *bufptr++ = clip_sample_16(sample + amp); |
995 | if (bufptr > pcmbuf_end) | 997 | if (bufptr >= pcmbuf_end) |
996 | bufptr = (int16_t *)audiobuffer; | 998 | bufptr = (int16_t *)audiobuffer; |
997 | 999 | ||
998 | /* Toggle square wave edge */ | 1000 | phase += step; |
999 | if (++count >= interval) | ||
1000 | { | ||
1001 | count = 0; | ||
1002 | amplitude = -amplitude; | ||
1003 | } | ||
1004 | } | 1001 | } |
1005 | 1002 | ||
1006 | /* Kick off playback if required */ | 1003 | /* Kick off playback if required */ |
@@ -1009,7 +1006,7 @@ void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) | |||
1009 | pcm_play_data(NULL, (unsigned char *)bufstart, samples * 4); | 1006 | pcm_play_data(NULL, (unsigned char *)bufstart, samples * 4); |
1010 | } | 1007 | } |
1011 | } | 1008 | } |
1012 | 1009 | #endif /* HAVE_HARDWARE_BEEP */ | |
1013 | 1010 | ||
1014 | /* Returns pcm buffer usage in percents (0 to 100). */ | 1011 | /* Returns pcm buffer usage in percents (0 to 100). */ |
1015 | int pcmbuf_usage(void) | 1012 | int pcmbuf_usage(void) |
diff --git a/apps/playback.c b/apps/playback.c index b21a3c1d46..50c4017200 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -694,7 +694,7 @@ static void audio_skip(int direction) | |||
694 | if (playlist_check(ci.new_track + wps_offset + direction)) | 694 | if (playlist_check(ci.new_track + wps_offset + direction)) |
695 | { | 695 | { |
696 | if (global_settings.beep) | 696 | if (global_settings.beep) |
697 | pcmbuf_beep(5000, 100, 2500*global_settings.beep); | 697 | pcmbuf_beep(2000, 100, 2500*global_settings.beep); |
698 | 698 | ||
699 | LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction); | 699 | LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction); |
700 | queue_post(&audio_queue, Q_AUDIO_SKIP, direction); | 700 | queue_post(&audio_queue, Q_AUDIO_SKIP, direction); |
@@ -706,7 +706,7 @@ static void audio_skip(int direction) | |||
706 | { | 706 | { |
707 | /* No more tracks. */ | 707 | /* No more tracks. */ |
708 | if (global_settings.beep) | 708 | if (global_settings.beep) |
709 | pcmbuf_beep(1000, 100, 1000*global_settings.beep); | 709 | pcmbuf_beep(1000, 100, 1500*global_settings.beep); |
710 | } | 710 | } |
711 | } | 711 | } |
712 | 712 | ||
diff --git a/firmware/drivers/audio/wm8751.c b/firmware/drivers/audio/wm8751.c index 9d552b505f..2e0eb06dbf 100644 --- a/firmware/drivers/audio/wm8751.c +++ b/firmware/drivers/audio/wm8751.c | |||
@@ -152,6 +152,17 @@ void audiohw_postinit(void) | |||
152 | wmcodec_write(LEFTMIX1, LEFTMIX1_LD2LO | LEFTMIX1_LI2LO_DEFAULT); | 152 | wmcodec_write(LEFTMIX1, LEFTMIX1_LD2LO | LEFTMIX1_LI2LO_DEFAULT); |
153 | wmcodec_write(RIGHTMIX2, RIGHTMIX2_RD2RO | RIGHTMIX2_RI2RO_DEFAULT); | 153 | wmcodec_write(RIGHTMIX2, RIGHTMIX2_RD2RO | RIGHTMIX2_RI2RO_DEFAULT); |
154 | 154 | ||
155 | #ifdef TOSHIBA_GIGABEAT_F | ||
156 | #ifdef HAVE_HARDWARE_BEEP | ||
157 | /* Single-ended mono input */ | ||
158 | wmcodec_write(MONOMIX1, 0); | ||
159 | |||
160 | /* Route mono input to both outputs at 0dB */ | ||
161 | wmcodec_write(LEFTMIX2, LEFTMIX2_MI2LO | LEFTMIX2_MI2LOVOL(2)); | ||
162 | wmcodec_write(RIGHTMIX1, RIGHTMIX1_MI2RO | RIGHTMIX1_MI2ROVOL(2)); | ||
163 | #endif | ||
164 | #endif | ||
165 | |||
155 | audiohw_mute(false); | 166 | audiohw_mute(false); |
156 | 167 | ||
157 | #ifdef MROBE_100 | 168 | #ifdef MROBE_100 |
diff --git a/firmware/export/config-gigabeat.h b/firmware/export/config-gigabeat.h index 313bdadcdd..664befd54e 100644 --- a/firmware/export/config-gigabeat.h +++ b/firmware/export/config-gigabeat.h | |||
@@ -52,6 +52,11 @@ | |||
52 | #define LCD_SLEEP_TIMEOUT (5*HZ) | 52 | #define LCD_SLEEP_TIMEOUT (5*HZ) |
53 | 53 | ||
54 | #define HAVE_TOUCHPAD_SENSITIVITY_SETTING | 54 | #define HAVE_TOUCHPAD_SENSITIVITY_SETTING |
55 | |||
56 | #ifndef SIMULATOR | ||
57 | #define HAVE_HARDWARE_BEEP | ||
58 | #endif | ||
59 | |||
55 | #endif /* BOOTLOADER */ | 60 | #endif /* BOOTLOADER */ |
56 | 61 | ||
57 | #define CONFIG_KEYPAD GIGABEAT_PAD | 62 | #define CONFIG_KEYPAD GIGABEAT_PAD |
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c index 76917c8c82..5ef8c8023a 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/kernel-meg-fx.c | |||
@@ -32,18 +32,18 @@ void tick_start(unsigned int interval_in_ms) | |||
32 | * Timer input clock frequency = | 32 | * Timer input clock frequency = |
33 | * fPCLK / {prescaler value+1} / {divider value} | 33 | * fPCLK / {prescaler value+1} / {divider value} |
34 | * TIMER_FREQ = 49156800 / 2 | 34 | * TIMER_FREQ = 49156800 / 2 |
35 | * 13300 = TIMER_FREQ / 231 / 8 | 35 | * 146300 = TIMER_FREQ / 21 / 8 |
36 | * 49156800 = 19*(11)*(7)*7*5*5*(3)*2*2*2*2*2*2 | 36 | * 49156800 = 19*11*(7)*7*5*5*(3)*2*2*2*2*2*2 |
37 | * 231 = 11*7*3 | 37 | * 21 = 7*3 |
38 | */ | 38 | */ |
39 | 39 | ||
40 | /* stop timer 4 */ | 40 | /* stop timer 4 */ |
41 | TCON &= ~(1 << 20); | 41 | TCON &= ~(1 << 20); |
42 | /* Set the count for timer 4 */ | 42 | /* Set the count for timer 4 */ |
43 | TCNTB4 = (TIMER_FREQ / 231 / 8) * interval_in_ms / 1000; | 43 | TCNTB4 = (TIMER_FREQ / TIMER234_PRESCALE / 8) * interval_in_ms / 1000; |
44 | /* Set the the prescaler value for timers 2,3, and 4 */ | 44 | /* Set the the prescaler value for timers 2,3, and 4 */ |
45 | TCFG0 = (TCFG0 & ~0xff00) | ((231-1) << 8); | 45 | TCFG0 = (TCFG0 & ~0xff00) | ((TIMER234_PRESCALE-1) << 8); |
46 | /* MUX4 = 1/16 */ | 46 | /* DMA mode off, MUX4 = 1/16 */ |
47 | TCFG1 = (TCFG1 & ~0xff0000) | 0x030000; | 47 | TCFG1 = (TCFG1 & ~0xff0000) | 0x030000; |
48 | /* set manual bit */ | 48 | /* set manual bit */ |
49 | TCON |= 1 << 21; | 49 | TCON |= 1 << 21; |
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h b/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h index ac195bf9a5..b5652a3365 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h +++ b/firmware/target/arm/s3c2440/gigabeat-fx/timer-target.h | |||
@@ -23,6 +23,7 @@ | |||
23 | 23 | ||
24 | /* timer is based on PCLK and minimum division is 2 */ | 24 | /* timer is based on PCLK and minimum division is 2 */ |
25 | #define TIMER_FREQ (49156800/2) | 25 | #define TIMER_FREQ (49156800/2) |
26 | #define TIMER234_PRESCALE 21 | ||
26 | 27 | ||
27 | bool __timer_set(long cycles, bool set); | 28 | bool __timer_set(long cycles, bool set); |
28 | bool __timer_register(void); | 29 | bool __timer_register(void); |
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c index 957d58b344..de965f0750 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/wmcodec-meg-fx.c | |||
@@ -29,8 +29,14 @@ | |||
29 | #include "kernel.h" | 29 | #include "kernel.h" |
30 | #include "sound.h" | 30 | #include "sound.h" |
31 | #include "i2c-meg-fx.h" | 31 | #include "i2c-meg-fx.h" |
32 | #include "system-target.h" | ||
33 | #include "timer-target.h" | ||
32 | #include "wmcodec.h" | 34 | #include "wmcodec.h" |
33 | 35 | ||
36 | #ifdef HAVE_HARDWARE_BEEP | ||
37 | static void beep_stop(void); | ||
38 | #endif | ||
39 | |||
34 | void audiohw_init(void) | 40 | void audiohw_init(void) |
35 | { | 41 | { |
36 | /* GPC5 controls headphone output */ | 42 | /* GPC5 controls headphone output */ |
@@ -39,6 +45,14 @@ void audiohw_init(void) | |||
39 | GPCDAT |= (1 << 5); | 45 | GPCDAT |= (1 << 5); |
40 | 46 | ||
41 | audiohw_preinit(); | 47 | audiohw_preinit(); |
48 | |||
49 | #ifdef HAVE_HARDWARE_BEEP | ||
50 | /* pin pullup ON */ | ||
51 | GPBUP &= ~(1 << 3); | ||
52 | beep_stop(); | ||
53 | /* set pin to TIMER3 output (functional TOUT3) */ | ||
54 | GPBCON = (GPBCON & ~(0x3 << 6)) | (2 << 6); | ||
55 | #endif | ||
42 | } | 56 | } |
43 | 57 | ||
44 | void wmcodec_write(int reg, int data) | 58 | void wmcodec_write(int reg, int data) |
@@ -48,3 +62,92 @@ void wmcodec_write(int reg, int data) | |||
48 | d[1] = data; | 62 | d[1] = data; |
49 | i2c_write(0x34, d, 2); | 63 | i2c_write(0x34, d, 2); |
50 | } | 64 | } |
65 | |||
66 | #ifdef HAVE_HARDWARE_BEEP | ||
67 | /** Beeping via TIMER3 output to codec MONO input **/ | ||
68 | static int beep_cycles = 0; | ||
69 | static int beep_cycle_count = 0; | ||
70 | |||
71 | static void beep_stop(void) | ||
72 | { | ||
73 | int oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); | ||
74 | |||
75 | /* stop interrupt */ | ||
76 | INTMSK |= TIMER3_MASK; | ||
77 | /* stop timer */ | ||
78 | TCON &= ~(1 << 16); | ||
79 | /* be sure timer PWM pin is LOW to avoid noise */ | ||
80 | TCON ^= (GPBDAT & (1 << 3)) << 15; | ||
81 | /* clear pending */ | ||
82 | SRCPND = TIMER3_MASK; | ||
83 | INTPND = TIMER3_MASK; | ||
84 | |||
85 | restore_interrupt(oldstatus); | ||
86 | } | ||
87 | |||
88 | /* Timer interrupt called on every cycle */ | ||
89 | void TIMER3(void) | ||
90 | { | ||
91 | if (++beep_cycles >= beep_cycle_count) | ||
92 | { | ||
93 | /* beep is complete */ | ||
94 | beep_stop(); | ||
95 | } | ||
96 | |||
97 | /* clear pending */ | ||
98 | SRCPND = TIMER3_MASK; | ||
99 | INTPND = TIMER3_MASK; | ||
100 | } | ||
101 | |||
102 | void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude) | ||
103 | { | ||
104 | #define TIMER3_TICK_SEC (TIMER_FREQ / TIMER234_PRESCALE) | ||
105 | |||
106 | unsigned long tcnt, tcmp; | ||
107 | int oldstatus; | ||
108 | |||
109 | if (amplitude <= 0) | ||
110 | { | ||
111 | beep_stop(); /* won't hear it anyway */ | ||
112 | return; | ||
113 | } | ||
114 | |||
115 | /* pretend this is pcm */ | ||
116 | if (amplitude > 32767) | ||
117 | amplitude = 32767; | ||
118 | |||
119 | /* limit frequency range to keep math in range */ | ||
120 | if (frequency > 19506) | ||
121 | frequency = 19506; | ||
122 | else if (frequency < 18) | ||
123 | frequency = 18; | ||
124 | |||
125 | /* set timer */ | ||
126 | tcnt = TIMER3_TICK_SEC / frequency; | ||
127 | |||
128 | oldstatus = disable_interrupt_save(IRQ_FIQ_STATUS); | ||
129 | |||
130 | beep_cycles = 0; | ||
131 | beep_cycle_count = TIMER3_TICK_SEC * duration / (tcnt*1000); | ||
132 | |||
133 | /* divider = 1/2 */ | ||
134 | TCFG1 = (TCFG1 & ~(0xf << 12)) | (0 << 12); | ||
135 | /* stop TIMER3, inverter OFF */ | ||
136 | TCON &= ~((1 << 18) | (1 << 16)); | ||
137 | /* set countdown */ | ||
138 | TCNTB3 = tcnt; | ||
139 | /* set PWM counter - control volume with duty cycle. */ | ||
140 | tcmp = tcnt*amplitude / (65536*2 - 2*amplitude); | ||
141 | TCMPB3 = tcmp < 1 ? 1 : tcmp; | ||
142 | /* manual update: on (to reset count), interval mode (auto reload) */ | ||
143 | TCON |= (1 << 19) | (1 << 17); | ||
144 | /* clear manual bit */ | ||
145 | TCON &= ~(1 << 17); | ||
146 | /* start timer */ | ||
147 | TCON |= (1 << 16); | ||
148 | /* enable timer interrupt */ | ||
149 | INTMSK &= ~TIMER3_MASK; | ||
150 | |||
151 | restore_interrupt(oldstatus); | ||
152 | } | ||
153 | #endif /* HAVE_HARDWARE_BEEP */ | ||