summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/sound/dac.cpp
blob: ee35c6d60a971525fb8e397b7cf6d35aa1bb24ef (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// license:BSD-3-Clause
// copyright-holders:smf
/***************************************************************************

    dac.cpp

    Four quadrant multiplying DAC.

    Binary Weighted Resistor Network, R-2R Ladder & PWM

    Binary, Ones Complement, or Twos Complement coding

***************************************************************************/

#include "emu.h"

#define DAC_GENERATOR_EPILOG(_dac_type, _dac_class, _dac_description, _dac_shortname) \
DEFINE_DEVICE_TYPE(_dac_type, _dac_class, _dac_shortname, _dac_description)

#include "dac.h"


//-------------------------------------------------
//  dac_mapper_unsigned - map an unsigned value of
//  the given number of bits to a sample value
//-------------------------------------------------

stream_buffer::sample_t dac_mapper_unsigned(u32 input, u8 bits)
{
	stream_buffer::sample_t scale = 1.0 / stream_buffer::sample_t((bits > 1) ? (1 << bits) : 1);
	input &= (1 << bits) - 1;
	return stream_buffer::sample_t(input) * scale;
}


//-------------------------------------------------
//  dac_mapper_signed - map a signed (2s complement)
//  value of the given number of bits to a sample value
//-------------------------------------------------

stream_buffer::sample_t dac_mapper_signed(u32 input, u8 bits)
{
	return dac_mapper_unsigned(input ^ (1 << (bits - 1)), bits);
}


//-------------------------------------------------
//  dac_mapper_ones_complement - map a value where
//  the top bit indicates the lower bits should be
//  treated as a negative 1s complement
//-------------------------------------------------

stream_buffer::sample_t dac_mapper_ones_complement(u32 input, u8 bits)
{
	// this mapping assumes symmetric reference voltages,
	// which is true for all existing cases
	if (BIT(input, bits - 1))
		return 0.5 - 0.5 * dac_mapper_unsigned(~input, bits - 1);
	else
		return 0.5 + 0.5 * dac_mapper_unsigned(input, bits - 1);
}


//-------------------------------------------------
//  dac_device_base - constructor
//-------------------------------------------------

dac_device_base::dac_device_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, const XTAL &clock, u8 bits, dac_mapper_callback mapper, stream_buffer::sample_t gain) :
	device_t(mconfig, type, tag, owner, clock),
	device_sound_interface(mconfig, *this),
	m_stream(nullptr),
	m_curval(0),
	m_value_map(1 << bits),
	m_bits(bits),
	m_mapper(mapper),
	m_gain(gain),
	m_range_min((bits == 1) ? 0.0 : -1.0),
	m_range_max(1.0)
{
}


//-------------------------------------------------
//  device_start - device startup
//-------------------------------------------------

void dac_device_base::device_start()
{
	// precompute all gain-applied values
	for (s32 code = 0; code < m_value_map.size(); code++)
		m_value_map[code] = m_mapper(code, m_bits) * m_gain;

	// determine the number of inputs
	int inputs = (m_specified_inputs_mask == 0) ? 0 : 2;

	// create the stream
	m_stream = stream_alloc(inputs, 1, 48000 * 4);

	// save data
	save_item(NAME(m_curval));
}


//-------------------------------------------------
//  sound_stream_update - stream updates
//-------------------------------------------------

void dac_device_base::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
{
	auto &out = outputs[0];

	// rails are constant
	if (inputs.size() == 0)
	{
		out.fill(m_range_min + m_curval * (m_range_max - m_range_min));
		return;
	}

	auto &hi = inputs[DAC_INPUT_RANGE_HI];
	auto &lo = inputs[DAC_INPUT_RANGE_LO];

	// constant lo, streaming hi
	if (!BIT(m_specified_inputs_mask, DAC_INPUT_RANGE_LO))
	{
		for (int sampindex = 0; sampindex < out.samples(); sampindex++)
			out.put(sampindex, m_range_min + m_curval * (hi.get(sampindex) - m_range_min));
	}

	// constant hi, streaming lo
	else if (!BIT(m_specified_inputs_mask, DAC_INPUT_RANGE_HI))
	{
		for (int sampindex = 0; sampindex < out.samples(); sampindex++)
			out.put(sampindex, lo.get(sampindex) + m_curval * (m_range_max - lo.get(sampindex)));
	}

	// both streams provided
	else
	{
		for (int sampindex = 0; sampindex < out.samples(); sampindex++)
			out.put(sampindex, lo.get(sampindex) + m_curval * (hi.get(sampindex) - lo.get(sampindex)));
	}
}