Commit fd06c37550b43980e8d641bb109c219cbe001d13
1 parent
52328140
PC speaker emulation (Joachim Henke)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1851 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
7 changed files
with
177 additions
and
20 deletions
Changelog
Makefile.target
... | ... | @@ -310,7 +310,7 @@ VL_OBJS+= ne2000.o rtl8139.o |
310 | 310 | ifeq ($(TARGET_BASE_ARCH), i386) |
311 | 311 | # Hardware support |
312 | 312 | VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) |
313 | -VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pc.o | |
313 | +VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259.o i8254.o pcspk.o pc.o | |
314 | 314 | VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o |
315 | 315 | DEFINES += -DHAS_AUDIO |
316 | 316 | endif | ... | ... |
hw/i8254.c
... | ... | @@ -209,6 +209,18 @@ int pit_get_gate(PITState *pit, int channel) |
209 | 209 | return s->gate; |
210 | 210 | } |
211 | 211 | |
212 | +int pit_get_initial_count(PITState *pit, int channel) | |
213 | +{ | |
214 | + PITChannelState *s = &pit->channels[channel]; | |
215 | + return s->count; | |
216 | +} | |
217 | + | |
218 | +int pit_get_mode(PITState *pit, int channel) | |
219 | +{ | |
220 | + PITChannelState *s = &pit->channels[channel]; | |
221 | + return s->mode; | |
222 | +} | |
223 | + | |
212 | 224 | static inline void pit_load_count(PITChannelState *s, int val) |
213 | 225 | { |
214 | 226 | if (val == 0) | ... | ... |
hw/pc.c
... | ... | @@ -36,8 +36,6 @@ |
36 | 36 | #define KERNEL_PARAMS_ADDR 0x00090000 |
37 | 37 | #define KERNEL_CMDLINE_ADDR 0x00099000 |
38 | 38 | |
39 | -int speaker_data_on; | |
40 | -int dummy_refresh_clock; | |
41 | 39 | static fdctrl_t *floppy_controller; |
42 | 40 | static RTCState *rtc_state; |
43 | 41 | static PITState *pit; |
... | ... | @@ -273,21 +271,6 @@ static void cmos_init(int ram_size, int boot_device, BlockDriverState **hd_table |
273 | 271 | // rtc_set_memory(s, 0x38, 1); |
274 | 272 | } |
275 | 273 | |
276 | -static void speaker_ioport_write(void *opaque, uint32_t addr, uint32_t val) | |
277 | -{ | |
278 | - speaker_data_on = (val >> 1) & 1; | |
279 | - pit_set_gate(pit, 2, val & 1); | |
280 | -} | |
281 | - | |
282 | -static uint32_t speaker_ioport_read(void *opaque, uint32_t addr) | |
283 | -{ | |
284 | - int out; | |
285 | - out = pit_get_out(pit, 2, qemu_get_clock(vm_clock)); | |
286 | - dummy_refresh_clock ^= 1; | |
287 | - return (speaker_data_on << 1) | pit_get_gate(pit, 2) | (out << 5) | | |
288 | - (dummy_refresh_clock << 4); | |
289 | -} | |
290 | - | |
291 | 274 | void ioport_set_a20(int enable) |
292 | 275 | { |
293 | 276 | /* XXX: send to all CPUs ? */ |
... | ... | @@ -783,8 +766,6 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, |
783 | 766 | } |
784 | 767 | |
785 | 768 | rtc_state = rtc_init(0x70, 8); |
786 | - register_ioport_read(0x61, 1, 1, speaker_ioport_read, NULL); | |
787 | - register_ioport_write(0x61, 1, 1, speaker_ioport_write, NULL); | |
788 | 769 | |
789 | 770 | register_ioport_read(0x92, 1, 1, ioport92_read, NULL); |
790 | 771 | register_ioport_write(0x92, 1, 1, ioport92_write, NULL); |
... | ... | @@ -794,6 +775,7 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, |
794 | 775 | } |
795 | 776 | isa_pic = pic_init(pic_irq_request, first_cpu); |
796 | 777 | pit = pit_init(0x40, 0); |
778 | + pcspk_init(pit); | |
797 | 779 | if (pci_enabled) { |
798 | 780 | pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); |
799 | 781 | } | ... | ... |
hw/pcspk.c
0 → 100644
1 | +/* | |
2 | + * QEMU PC speaker emulation | |
3 | + * | |
4 | + * Copyright (c) 2006 Joachim Henke | |
5 | + * | |
6 | + * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | + * of this software and associated documentation files (the "Software"), to deal | |
8 | + * in the Software without restriction, including without limitation the rights | |
9 | + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | + * copies of the Software, and to permit persons to whom the Software is | |
11 | + * furnished to do so, subject to the following conditions: | |
12 | + * | |
13 | + * The above copyright notice and this permission notice shall be included in | |
14 | + * all copies or substantial portions of the Software. | |
15 | + * | |
16 | + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | + * THE SOFTWARE. | |
23 | + */ | |
24 | + | |
25 | +#include "vl.h" | |
26 | + | |
27 | +#define PCSPK_BUF_LEN 1792 | |
28 | +#define PCSPK_SAMPLE_RATE 32000 | |
29 | +#define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1) | |
30 | +#define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ) | |
31 | + | |
32 | +typedef struct { | |
33 | + uint8_t sample_buf[PCSPK_BUF_LEN]; | |
34 | + QEMUSoundCard card; | |
35 | + SWVoiceOut *voice; | |
36 | + PITState *pit; | |
37 | + unsigned int pit_count; | |
38 | + unsigned int samples; | |
39 | + unsigned int play_pos; | |
40 | + int data_on; | |
41 | + int dummy_refresh_clock; | |
42 | +} PCSpkState; | |
43 | + | |
44 | +static const char *s_spk = "pcspk"; | |
45 | +static PCSpkState pcspk_state; | |
46 | + | |
47 | +static inline void generate_samples(PCSpkState *s) | |
48 | +{ | |
49 | + unsigned int i; | |
50 | + | |
51 | + if (s->pit_count) { | |
52 | + const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count; | |
53 | + const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m; | |
54 | + | |
55 | + /* multiple of wavelength for gapless looping */ | |
56 | + s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1; | |
57 | + for (i = 0; i < s->samples; ++i) | |
58 | + s->sample_buf[i] = (64 & (n * i >> 25)) - 32; | |
59 | + } else { | |
60 | + s->samples = PCSPK_BUF_LEN; | |
61 | + for (i = 0; i < PCSPK_BUF_LEN; ++i) | |
62 | + s->sample_buf[i] = 128; /* silence */ | |
63 | + } | |
64 | +} | |
65 | + | |
66 | +static void pcspk_callback(void *opaque, int free) | |
67 | +{ | |
68 | + PCSpkState *s = opaque; | |
69 | + unsigned int n; | |
70 | + | |
71 | + if (pit_get_mode(s->pit, 2) != 3) | |
72 | + return; | |
73 | + | |
74 | + n = pit_get_initial_count(s->pit, 2); | |
75 | + /* avoid frequencies that are not reproducible with sample rate */ | |
76 | + if (n < PCSPK_MIN_COUNT) | |
77 | + n = 0; | |
78 | + | |
79 | + if (s->pit_count != n) { | |
80 | + s->pit_count = n; | |
81 | + s->play_pos = 0; | |
82 | + generate_samples(s); | |
83 | + } | |
84 | + | |
85 | + while (free > 0) { | |
86 | + n = audio_MIN(s->samples - s->play_pos, (unsigned int)free); | |
87 | + n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n); | |
88 | + if (!n) | |
89 | + break; | |
90 | + s->play_pos = (s->play_pos + n) % s->samples; | |
91 | + free -= n; | |
92 | + } | |
93 | +} | |
94 | + | |
95 | +int pcspk_audio_init(AudioState *audio) | |
96 | +{ | |
97 | + PCSpkState *s = &pcspk_state; | |
98 | + audsettings_t as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8}; | |
99 | + | |
100 | + if (!audio) { | |
101 | + AUD_log(s_spk, "No audio state\n"); | |
102 | + return -1; | |
103 | + } | |
104 | + AUD_register_card(audio, s_spk, &s->card); | |
105 | + | |
106 | + s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as, 0); | |
107 | + if (!s->voice) { | |
108 | + AUD_log(s_spk, "Could not open voice\n"); | |
109 | + return -1; | |
110 | + } | |
111 | + | |
112 | + return 0; | |
113 | +} | |
114 | + | |
115 | +static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr) | |
116 | +{ | |
117 | + PCSpkState *s = opaque; | |
118 | + int out; | |
119 | + | |
120 | + s->dummy_refresh_clock ^= (1 << 4); | |
121 | + out = pit_get_out(s->pit, 2, qemu_get_clock(vm_clock)) << 5; | |
122 | + | |
123 | + return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out; | |
124 | +} | |
125 | + | |
126 | +static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) | |
127 | +{ | |
128 | + PCSpkState *s = opaque; | |
129 | + const int gate = val & 1; | |
130 | + | |
131 | + s->data_on = (val >> 1) & 1; | |
132 | + pit_set_gate(s->pit, 2, gate); | |
133 | + if (s->voice) { | |
134 | + if (gate) /* restart */ | |
135 | + s->play_pos = 0; | |
136 | + AUD_set_active_out(s->voice, gate & s->data_on); | |
137 | + } | |
138 | +} | |
139 | + | |
140 | +void pcspk_init(PITState *pit) | |
141 | +{ | |
142 | + PCSpkState *s = &pcspk_state; | |
143 | + | |
144 | + s->pit = pit; | |
145 | + register_ioport_read(0x61, 1, 1, pcspk_ioport_read, s); | |
146 | + register_ioport_write(0x61, 1, 1, pcspk_ioport_write, s); | |
147 | +} | ... | ... |
vl.c
... | ... | @@ -4862,6 +4862,15 @@ void register_machines(void) |
4862 | 4862 | |
4863 | 4863 | #ifdef HAS_AUDIO |
4864 | 4864 | struct soundhw soundhw[] = { |
4865 | +#ifdef TARGET_I386 | |
4866 | + { | |
4867 | + "pcspk", | |
4868 | + "PC speaker", | |
4869 | + 0, | |
4870 | + 1, | |
4871 | + { .init_isa = pcspk_audio_init } | |
4872 | + }, | |
4873 | +#endif | |
4865 | 4874 | { |
4866 | 4875 | "sb16", |
4867 | 4876 | "Creative Sound Blaster 16", | ... | ... |
vl.h
... | ... | @@ -819,8 +819,14 @@ typedef struct PITState PITState; |
819 | 819 | PITState *pit_init(int base, int irq); |
820 | 820 | void pit_set_gate(PITState *pit, int channel, int val); |
821 | 821 | int pit_get_gate(PITState *pit, int channel); |
822 | +int pit_get_initial_count(PITState *pit, int channel); | |
823 | +int pit_get_mode(PITState *pit, int channel); | |
822 | 824 | int pit_get_out(PITState *pit, int channel, int64_t current_time); |
823 | 825 | |
826 | +/* pcspk.c */ | |
827 | +void pcspk_init(PITState *); | |
828 | +int pcspk_audio_init(AudioState *); | |
829 | + | |
824 | 830 | /* pc.c */ |
825 | 831 | extern QEMUMachine pc_machine; |
826 | 832 | extern QEMUMachine isapc_machine; | ... | ... |