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
@@ -2,6 +2,7 @@ version 0.8.1: | @@ -2,6 +2,7 @@ version 0.8.1: | ||
2 | 2 | ||
3 | - USB tablet support (Brad Campbell, Anthony Liguori) | 3 | - USB tablet support (Brad Campbell, Anthony Liguori) |
4 | - win32 host serial support (Kazu) | 4 | - win32 host serial support (Kazu) |
5 | + - PC speaker support (Joachim Henke) | ||
5 | 6 | ||
6 | version 0.8.0: | 7 | version 0.8.0: |
7 | 8 |
Makefile.target
@@ -310,7 +310,7 @@ VL_OBJS+= ne2000.o rtl8139.o | @@ -310,7 +310,7 @@ VL_OBJS+= ne2000.o rtl8139.o | ||
310 | ifeq ($(TARGET_BASE_ARCH), i386) | 310 | ifeq ($(TARGET_BASE_ARCH), i386) |
311 | # Hardware support | 311 | # Hardware support |
312 | VL_OBJS+= ide.o pckbd.o ps2.o vga.o $(SOUND_HW) dma.o $(AUDIODRV) | 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 | VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o | 314 | VL_OBJS+= cirrus_vga.o mixeng.o apic.o parallel.o |
315 | DEFINES += -DHAS_AUDIO | 315 | DEFINES += -DHAS_AUDIO |
316 | endif | 316 | endif |
hw/i8254.c
@@ -209,6 +209,18 @@ int pit_get_gate(PITState *pit, int channel) | @@ -209,6 +209,18 @@ int pit_get_gate(PITState *pit, int channel) | ||
209 | return s->gate; | 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 | static inline void pit_load_count(PITChannelState *s, int val) | 224 | static inline void pit_load_count(PITChannelState *s, int val) |
213 | { | 225 | { |
214 | if (val == 0) | 226 | if (val == 0) |
hw/pc.c
@@ -36,8 +36,6 @@ | @@ -36,8 +36,6 @@ | ||
36 | #define KERNEL_PARAMS_ADDR 0x00090000 | 36 | #define KERNEL_PARAMS_ADDR 0x00090000 |
37 | #define KERNEL_CMDLINE_ADDR 0x00099000 | 37 | #define KERNEL_CMDLINE_ADDR 0x00099000 |
38 | 38 | ||
39 | -int speaker_data_on; | ||
40 | -int dummy_refresh_clock; | ||
41 | static fdctrl_t *floppy_controller; | 39 | static fdctrl_t *floppy_controller; |
42 | static RTCState *rtc_state; | 40 | static RTCState *rtc_state; |
43 | static PITState *pit; | 41 | static PITState *pit; |
@@ -273,21 +271,6 @@ static void cmos_init(int ram_size, int boot_device, BlockDriverState **hd_table | @@ -273,21 +271,6 @@ static void cmos_init(int ram_size, int boot_device, BlockDriverState **hd_table | ||
273 | // rtc_set_memory(s, 0x38, 1); | 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 | void ioport_set_a20(int enable) | 274 | void ioport_set_a20(int enable) |
292 | { | 275 | { |
293 | /* XXX: send to all CPUs ? */ | 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,8 +766,6 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, | ||
783 | } | 766 | } |
784 | 767 | ||
785 | rtc_state = rtc_init(0x70, 8); | 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 | register_ioport_read(0x92, 1, 1, ioport92_read, NULL); | 770 | register_ioport_read(0x92, 1, 1, ioport92_read, NULL); |
790 | register_ioport_write(0x92, 1, 1, ioport92_write, NULL); | 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,6 +775,7 @@ static void pc_init1(int ram_size, int vga_ram_size, int boot_device, | ||
794 | } | 775 | } |
795 | isa_pic = pic_init(pic_irq_request, first_cpu); | 776 | isa_pic = pic_init(pic_irq_request, first_cpu); |
796 | pit = pit_init(0x40, 0); | 777 | pit = pit_init(0x40, 0); |
778 | + pcspk_init(pit); | ||
797 | if (pci_enabled) { | 779 | if (pci_enabled) { |
798 | pic_set_alt_irq_func(isa_pic, ioapic_set_irq, ioapic); | 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,6 +4862,15 @@ void register_machines(void) | ||
4862 | 4862 | ||
4863 | #ifdef HAS_AUDIO | 4863 | #ifdef HAS_AUDIO |
4864 | struct soundhw soundhw[] = { | 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 | "sb16", | 4875 | "sb16", |
4867 | "Creative Sound Blaster 16", | 4876 | "Creative Sound Blaster 16", |
vl.h
@@ -819,8 +819,14 @@ typedef struct PITState PITState; | @@ -819,8 +819,14 @@ typedef struct PITState PITState; | ||
819 | PITState *pit_init(int base, int irq); | 819 | PITState *pit_init(int base, int irq); |
820 | void pit_set_gate(PITState *pit, int channel, int val); | 820 | void pit_set_gate(PITState *pit, int channel, int val); |
821 | int pit_get_gate(PITState *pit, int channel); | 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 | int pit_get_out(PITState *pit, int channel, int64_t current_time); | 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 | /* pc.c */ | 830 | /* pc.c */ |
825 | extern QEMUMachine pc_machine; | 831 | extern QEMUMachine pc_machine; |
826 | extern QEMUMachine isapc_machine; | 832 | extern QEMUMachine isapc_machine; |