|
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 "hw.h" |
|
26 #include "pc.h" |
|
27 #include "isa.h" |
|
28 #include "audio/audio.h" |
|
29 #include "qemu-timer.h" |
|
30 |
|
31 #define PCSPK_BUF_LEN 1792 |
|
32 #define PCSPK_SAMPLE_RATE 32000 |
|
33 #define PCSPK_MAX_FREQ (PCSPK_SAMPLE_RATE >> 1) |
|
34 #define PCSPK_MIN_COUNT ((PIT_FREQ + PCSPK_MAX_FREQ - 1) / PCSPK_MAX_FREQ) |
|
35 |
|
36 typedef struct { |
|
37 uint8_t sample_buf[PCSPK_BUF_LEN]; |
|
38 QEMUSoundCard card; |
|
39 SWVoiceOut *voice; |
|
40 PITState *pit; |
|
41 unsigned int pit_count; |
|
42 unsigned int samples; |
|
43 unsigned int play_pos; |
|
44 int data_on; |
|
45 int dummy_refresh_clock; |
|
46 } PCSpkState; |
|
47 |
|
48 static const char *s_spk = "pcspk"; |
|
49 static PCSpkState pcspk_state; |
|
50 |
|
51 static inline void generate_samples(PCSpkState *s) |
|
52 { |
|
53 unsigned int i; |
|
54 |
|
55 if (s->pit_count) { |
|
56 const uint32_t m = PCSPK_SAMPLE_RATE * s->pit_count; |
|
57 const uint32_t n = ((uint64_t)PIT_FREQ << 32) / m; |
|
58 |
|
59 /* multiple of wavelength for gapless looping */ |
|
60 s->samples = (PCSPK_BUF_LEN * PIT_FREQ / m * m / (PIT_FREQ >> 1) + 1) >> 1; |
|
61 for (i = 0; i < s->samples; ++i) |
|
62 s->sample_buf[i] = (64 & (n * i >> 25)) - 32; |
|
63 } else { |
|
64 s->samples = PCSPK_BUF_LEN; |
|
65 for (i = 0; i < PCSPK_BUF_LEN; ++i) |
|
66 s->sample_buf[i] = 128; /* silence */ |
|
67 } |
|
68 } |
|
69 |
|
70 static void pcspk_callback(void *opaque, int free) |
|
71 { |
|
72 PCSpkState *s = opaque; |
|
73 unsigned int n; |
|
74 |
|
75 if (pit_get_mode(s->pit, 2) != 3) |
|
76 return; |
|
77 |
|
78 n = pit_get_initial_count(s->pit, 2); |
|
79 /* avoid frequencies that are not reproducible with sample rate */ |
|
80 if (n < PCSPK_MIN_COUNT) |
|
81 n = 0; |
|
82 |
|
83 if (s->pit_count != n) { |
|
84 s->pit_count = n; |
|
85 s->play_pos = 0; |
|
86 generate_samples(s); |
|
87 } |
|
88 |
|
89 while (free > 0) { |
|
90 n = audio_MIN(s->samples - s->play_pos, (unsigned int)free); |
|
91 n = AUD_write(s->voice, &s->sample_buf[s->play_pos], n); |
|
92 if (!n) |
|
93 break; |
|
94 s->play_pos = (s->play_pos + n) % s->samples; |
|
95 free -= n; |
|
96 } |
|
97 } |
|
98 |
|
99 int pcspk_audio_init(AudioState *audio, qemu_irq *pic) |
|
100 { |
|
101 PCSpkState *s = &pcspk_state; |
|
102 struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0}; |
|
103 |
|
104 if (!audio) { |
|
105 AUD_log(s_spk, "No audio state\n"); |
|
106 return -1; |
|
107 } |
|
108 AUD_register_card(audio, s_spk, &s->card); |
|
109 |
|
110 s->voice = AUD_open_out(&s->card, s->voice, s_spk, s, pcspk_callback, &as); |
|
111 if (!s->voice) { |
|
112 AUD_log(s_spk, "Could not open voice\n"); |
|
113 return -1; |
|
114 } |
|
115 |
|
116 return 0; |
|
117 } |
|
118 |
|
119 static uint32_t pcspk_ioport_read(void *opaque, uint32_t addr) |
|
120 { |
|
121 PCSpkState *s = opaque; |
|
122 int out; |
|
123 |
|
124 s->dummy_refresh_clock ^= (1 << 4); |
|
125 out = pit_get_out(s->pit, 2, qemu_get_clock(vm_clock)) << 5; |
|
126 |
|
127 return pit_get_gate(s->pit, 2) | (s->data_on << 1) | s->dummy_refresh_clock | out; |
|
128 } |
|
129 |
|
130 static void pcspk_ioport_write(void *opaque, uint32_t addr, uint32_t val) |
|
131 { |
|
132 PCSpkState *s = opaque; |
|
133 const int gate = val & 1; |
|
134 |
|
135 s->data_on = (val >> 1) & 1; |
|
136 pit_set_gate(s->pit, 2, gate); |
|
137 if (s->voice) { |
|
138 if (gate) /* restart */ |
|
139 s->play_pos = 0; |
|
140 AUD_set_active_out(s->voice, gate & s->data_on); |
|
141 } |
|
142 } |
|
143 |
|
144 void pcspk_init(PITState *pit) |
|
145 { |
|
146 PCSpkState *s = &pcspk_state; |
|
147 |
|
148 s->pit = pit; |
|
149 register_ioport_read(0x61, 1, 1, pcspk_ioport_read, s); |
|
150 register_ioport_write(0x61, 1, 1, pcspk_ioport_write, s); |
|
151 } |