1 // license:BSD-3-Clause
2 // copyright-holders:K.Wilkins,Couriersud,Derrick Renaud,Frank Palazzolo
3 /************************************************************************
4  *
5  *  MAME - Discrete sound system emulation library
6  *
7  *  Written by K.Wilkins (mame@esplexo.co.uk)
8  *
9  *  (c) K.Wilkins 2000
10  *
11  *  Coding started in November 2000
12  *  KW - Added Sawtooth waveforms  Feb2003
13  *
14  ***********************************************************************
15  *
16  * SEE DISCRETE.H for documentation on usage
17  *
18  ***********************************************************************
19  *
20  * Each sound primative DSS_xxxx or DST_xxxx has its own implementation
21  * file. All discrete sound primatives MUST implement the following
22  * API:
23  *
24  * dsX_NAME_step(inputs, context, float timestep)  - Perform time step
25  *                                                  return output value
26  * dsX_NAME_reset(context) - Reset to initial state
27  *
28  * Core software takes care of traversing the netlist in the correct
29  * order
30  *
31  * device_start                - Read Node list, initialise & reset
32  * device_stop                 - Shutdown discrete sound system
33  * device_reset                - Put sound system back to time 0
34  * discrete_stream_update()    - This does the real update to the sim
35  *
36  ************************************************************************/
37 
38 #include "emu.h"
39 #include "discrete.h"
40 
41 #include "wavwrite.h"
42 
43 #include <atomic>
44 #include <iostream>
45 
46 
47 // device type definition
48 DEFINE_DEVICE_TYPE(DISCRETE, discrete_sound_device, "discrete", "Discrete Sound")
49 
50 /*************************************
51  *
52  *  Performance
53  *
54  *************************************/
55 
56 /*
57  * Normally, the discrete core processes 960 samples per update.
58  * With the various buffers involved, this on a Core2 is not as
59  * performant as processing 240 samples 4 times.
60  * The setting most probably depends on CPU and which modules are
61  * run and how many tasks are defined.
62  *
63  * Values < 32 exhibit poor performance (too much overhead) while
64  * Values > 500 have a slightly worse performace (too much cache misses?).
65  */
66 
67 #define MAX_SAMPLES_PER_TASK_SLICE  (960/4)
68 
69 /*************************************
70  *
71  *  Debugging
72  *
73  *************************************/
74 
75 #define DISCRETE_DEBUGLOG           (0)
76 
77 /*************************************
78  *
79  *  Use tasks ?
80  *
81  *************************************/
82 
83 #define USE_DISCRETE_TASKS          (1)
84 
85 /*************************************
86  *
87  *  Internal classes
88  *
89  *************************************/
90 
91 struct output_buffer
92 {
93 	std::unique_ptr<double []>  node_buf;
94 	const double                *source;
95 	volatile double             *ptr;
96 	int                         node_num;
97 };
98 
99 struct input_buffer
100 {
101 	volatile const double       *ptr;               /* pointer into linked_outbuf.nodebuf */
102 	output_buffer *             linked_outbuf;      /* what output are we connected to ? */
103 	double                      buffer;             /* input[] will point here */
104 };
105 
106 class discrete_task
107 {
108 	friend class discrete_device;
109 public:
~discrete_task()110 	virtual ~discrete_task() { }
111 
112 	inline void step_nodes();
lock_threadid(int32_t threadid)113 	inline bool lock_threadid(int32_t threadid)
114 	{
115 		int expected = -1;
116 		return m_threadid.compare_exchange_weak(expected, threadid, std::memory_order_release,std::memory_order_relaxed);
117 	}
unlock()118 	inline void unlock() { m_threadid = -1; }
119 
120 	//const linked_list_entry *list;
121 	discrete_device::node_step_list_t        step_list;
122 
123 	/* list of source nodes */
124 	std::vector<input_buffer> source_list;      /* discrete_source_node */
125 
126 	int task_group = 0;
127 
128 
discrete_task(discrete_device & pdev)129 	discrete_task(discrete_device &pdev) : m_device(pdev), m_threadid(-1)
130 	{
131 		// FIXME: the code expects to be able to take pointers to members of elements of this vector before it's filled
132 		source_list.reserve(16);
133 	}
134 
135 protected:
136 	static void *task_callback(void *param, int threadid);
137 	inline bool process();
138 
139 	void check(discrete_task &dest_task);
140 	void prepare_for_queue(int samples);
141 
142 	std::vector<output_buffer>      m_buffers;
143 	discrete_device &                   m_device;
144 
145 private:
146 	std::atomic<int32_t>      m_threadid;
147 	volatile int            m_samples = 0;
148 };
149 
150 
151 /*************************************
152  *
153  *  Included simulation objects
154  *
155  *************************************/
156 
157 #include "disc_sys.hxx"       /* discrete core modules and support functions */
158 #include "disc_wav.hxx"       /* Wave sources   - SINE/SQUARE/NOISE/etc */
159 #include "disc_mth.hxx"       /* Math Devices   - ADD/GAIN/etc */
160 #include "disc_inp.hxx"       /* Input Devices  - INPUT/CONST/etc */
161 #include "disc_flt.hxx"       /* Filter Devices - RCF/HPF/LPF */
162 #include "disc_dev.hxx"       /* Popular Devices - NE555/etc */
163 
164 /*************************************
165  *
166  *  INLINEs
167  *
168  *************************************/
169 
170 
171 
172 /*************************************
173  *
174  *  Task implementation
175  *
176  *************************************/
177 
step_nodes()178 inline void discrete_task::step_nodes()
179 {
180 	for (input_buffer &sn : source_list)
181 	{
182 		sn.buffer = *sn.ptr++;
183 	}
184 
185 	if (EXPECTED(!m_device.profiling()))
186 	{
187 		for (discrete_step_interface *entry : step_list)
188 		{
189 			/* Now step the node */
190 			entry->step();
191 		}
192 	}
193 	else
194 	{
195 		osd_ticks_t last = get_profile_ticks();
196 
197 		for (discrete_step_interface *node : step_list)
198 		{
199 			node->run_time -= last;
200 			node->step();
201 			last = get_profile_ticks();
202 			node->run_time += last;
203 		}
204 	}
205 
206 	/* buffer the outputs */
207 	for (output_buffer &outbuf : m_buffers)
208 		*outbuf.ptr++ = *outbuf.source;
209 }
210 
task_callback(void * param,int threadid)211 void *discrete_task::task_callback(void *param, int threadid)
212 {
213 	const auto &list = *reinterpret_cast<const discrete_sound_device::task_list_t *>(param);
214 	do
215 	{
216 		for (const auto &task : list)
217 		{
218 			/* try to lock */
219 			if (task->lock_threadid(threadid))
220 			{
221 				if (!task->process())
222 					return nullptr;
223 				task->unlock();
224 			}
225 		}
226 	} while (1);
227 
228 	return nullptr;
229 }
230 
process()231 bool discrete_task::process()
232 {
233 	int samples = std::min(int(m_samples), MAX_SAMPLES_PER_TASK_SLICE);
234 
235 	/* check dependencies */
236 	for (input_buffer &sn : source_list)
237 	{
238 		int avail = sn.linked_outbuf->ptr - sn.ptr;
239 		if (avail < 0)
240 			throw emu_fatalerror("discrete_task::process: available samples are negative");
241 		if (avail < samples)
242 			samples = avail;
243 	}
244 
245 	m_samples -= samples;
246 	if (m_samples < 0)
247 		throw emu_fatalerror("discrete_task::process: m_samples got negative");
248 	while (samples > 0)
249 	{
250 		/* step */
251 		step_nodes();
252 		samples--;
253 	}
254 	if (m_samples == 0)
255 	{
256 		/* return and keep the task locked so it is not picked up by other worker threads */
257 		return false;
258 	}
259 	return true;
260 }
261 
prepare_for_queue(int samples)262 void discrete_task::prepare_for_queue(int samples)
263 {
264 	m_samples = samples;
265 	/* set up task buffers */
266 	for (output_buffer &ob : m_buffers)
267 		ob.ptr = ob.node_buf.get();
268 
269 	/* initialize sources */
270 	for (input_buffer &sn : source_list)
271 	{
272 		sn.ptr = sn.linked_outbuf->node_buf.get();
273 	}
274 }
275 
check(discrete_task & dest_task)276 void discrete_task::check(discrete_task &dest_task)
277 {
278 	// FIXME: this function takes addresses of elements of a vector that has items added later
279 	// 16 is enough for the systems in MAME, but the code should be fixed properly
280 	m_buffers.reserve(16);
281 
282 	/* Determine, which nodes in the task are referenced by nodes in dest_task
283 	 * and add them to the list of nodes to be buffered for further processing
284 	 */
285 	for (discrete_step_interface *node_entry : step_list)
286 	{
287 		discrete_base_node *task_node = node_entry->self;
288 
289 		for (discrete_step_interface *step_entry : dest_task.step_list)
290 		{
291 			discrete_base_node *dest_node = step_entry->self;
292 
293 			/* loop over all active inputs */
294 			for (int inputnum = 0; inputnum < dest_node->active_inputs(); inputnum++)
295 			{
296 				int inputnode_num = dest_node->input_node(inputnum);
297 				if IS_VALUE_A_NODE(inputnode_num)
298 				{
299 					/* Fixme: sub nodes ! */
300 					if (NODE_DEFAULT_NODE(task_node->block_node()) == NODE_DEFAULT_NODE(inputnode_num))
301 					{
302 						int found = -1;
303 						output_buffer *pbuf = nullptr;
304 
305 						for (int i = 0; i < m_buffers.size(); i++)
306 //                          if (m_buffers[i].node->block_node() == inputnode_num)
307 							if (m_buffers[i].node_num == inputnode_num)
308 							{
309 								found = i;
310 								pbuf = &m_buffers[i];
311 								break;
312 							}
313 
314 						if (found<0)
315 						{
316 							output_buffer buf;
317 
318 							buf.node_buf = std::make_unique<double []>((task_node->sample_rate() + sound_manager::STREAMS_UPDATE_FREQUENCY) / sound_manager::STREAMS_UPDATE_FREQUENCY);
319 							buf.ptr = buf.node_buf.get();
320 							buf.source = dest_node->m_input[inputnum];
321 							buf.node_num = inputnode_num;
322 							//buf.node = device->discrete_find_node(inputnode);
323 							m_buffers.push_back(std::move(buf));
324 							pbuf = &m_buffers.back();
325 						}
326 						m_device.discrete_log("dso_task_start - buffering %d(%d) in task %p group %d referenced by %d group %d", NODE_INDEX(inputnode_num), NODE_CHILD_NODE_NUM(inputnode_num), this, task_group, dest_node->index(), dest_task.task_group);
327 
328 						/* register into source list */
329 						dest_task.source_list.push_back(input_buffer{ nullptr, pbuf, 0.0 });
330 						// FIXME: taking address of element of vector before it's filled
331 						dest_node->m_input[inputnum] = &dest_task.source_list.back().buffer;
332 
333 					}
334 				}
335 			}
336 		}
337 	}
338 }
339 
340 /*************************************
341  *
342  *  Base node implementation
343  *
344  *************************************/
345 
discrete_base_node()346 discrete_base_node::discrete_base_node() :
347 	m_device(nullptr),
348 	m_block(nullptr),
349 	m_active_inputs(0),
350 	m_custom(nullptr),
351 	m_input_is_node(0),
352 	m_step_intf(nullptr),
353 	m_input_intf(nullptr),
354 	m_output_intf(nullptr)
355 {
356 	m_output[0] = 0.0;
357 }
358 
359 
~discrete_base_node()360 discrete_base_node::~discrete_base_node()
361 {
362 	/* currently noting */
363 }
364 
init(discrete_device * pdev,const discrete_block * xblock)365 void discrete_base_node::init(discrete_device *pdev, const discrete_block *xblock)
366 {
367 	m_device = pdev;
368 	m_block = xblock;
369 
370 	m_custom = m_block->custom;
371 	m_active_inputs = m_block->active_inputs;
372 
373 	m_step_intf = dynamic_cast<discrete_step_interface *>(this);
374 	m_input_intf = dynamic_cast<discrete_input_interface *>(this);
375 	m_output_intf = dynamic_cast<discrete_sound_output_interface *>(this);
376 
377 	if (m_step_intf)
378 	{
379 		m_step_intf->run_time = 0;
380 		m_step_intf->self = this;
381 	}
382 }
383 
save_state()384 void discrete_base_node::save_state()
385 {
386 	if (m_block->node != NODE_SPECIAL)
387 		m_device->save_item(NAME(m_output), m_block->node);
388 }
389 
discrete_find_node(int node)390 discrete_base_node *discrete_device::discrete_find_node(int node)
391 {
392 	if (node < NODE_START || node > NODE_END) return nullptr;
393 	return m_indexed_node[NODE_INDEX(node)];
394 }
395 
resolve_input_nodes()396 void discrete_base_node::resolve_input_nodes()
397 {
398 	int inputnum;
399 
400 	/* loop over all active inputs */
401 	for (inputnum = 0; inputnum < m_active_inputs; inputnum++)
402 	{
403 		int inputnode = m_block->input_node[inputnum];
404 
405 		/* if this input is node-based, find the node in the indexed list */
406 		if IS_VALUE_A_NODE(inputnode)
407 		{
408 			//discrete_base_node *node_ref = m_device->m_indexed_node[NODE_INDEX(inputnode)];
409 			discrete_base_node *node_ref = m_device->discrete_find_node(inputnode);
410 			if (!node_ref)
411 				fatalerror("discrete_start - NODE_%02d referenced a non existent node NODE_%02d\n", index(), NODE_INDEX(inputnode));
412 
413 			if ((NODE_CHILD_NODE_NUM(inputnode) >= node_ref->max_output()) /*&& (node_ref->module_type() != DST_CUSTOM)*/)
414 				fatalerror("discrete_start - NODE_%02d referenced non existent output %d on node NODE_%02d\n", index(), NODE_CHILD_NODE_NUM(inputnode), NODE_INDEX(inputnode));
415 
416 			m_input[inputnum] = &(node_ref->m_output[NODE_CHILD_NODE_NUM(inputnode)]);  /* Link referenced node out to input */
417 			m_input_is_node |= 1 << inputnum;           /* Bit flag if input is node */
418 		}
419 		else
420 		{
421 			/* warn if trying to use a node for an input that can only be static */
422 			if IS_VALUE_A_NODE(m_block->initial[inputnum])
423 			{
424 				m_device->discrete_log("Warning - discrete_start - NODE_%02d trying to use a node on static input %d",  index(), inputnum);
425 				/* also report it in the error log so it is not missed */
426 				m_device->logerror("Warning - discrete_start - NODE_%02d trying to use a node on static input %d",  index(), inputnum);
427 			}
428 			else
429 			{
430 				m_input[inputnum] = &(m_block->initial[inputnum]);
431 			}
432 		}
433 	}
434 	for (inputnum = m_active_inputs; inputnum < DISCRETE_MAX_INPUTS; inputnum++)
435 	{
436 		/* FIXME: Check that no nodes follow ! */
437 		m_input[inputnum] = &(m_block->initial[inputnum]);
438 	}
439 }
440 
node_output_ptr(int onode)441 const double *discrete_device::node_output_ptr(int onode)
442 {
443 	const discrete_base_node *node;
444 	node = discrete_find_node(onode);
445 
446 	if (node != nullptr)
447 	{
448 		return &(node->m_output[NODE_CHILD_NODE_NUM(onode)]);
449 	}
450 	else
451 		return nullptr;
452 }
453 
454 /*************************************
455  *
456  *  Device implementation
457  *
458  *************************************/
459 
460 
461 //-------------------------------------------------
462 //  discrete_log: Debug logging
463 //-------------------------------------------------
464 
discrete_log(const char * text,...) const465 void CLIB_DECL discrete_device::discrete_log(const char *text, ...) const
466 {
467 	if (DISCRETE_DEBUGLOG)
468 	{
469 		va_list arg;
470 		va_start(arg, text);
471 
472 		if(m_disclogfile)
473 		{
474 			vfprintf(m_disclogfile, text, arg);
475 			fprintf(m_disclogfile, "\n");
476 			fflush(m_disclogfile);
477 		}
478 
479 		va_end(arg);
480 	}
481 }
482 
483 //-------------------------------------------------
484 //  discrete_build_list: Build import list
485 //-------------------------------------------------
486 
discrete_build_list(const discrete_block * intf,sound_block_list_t & block_list)487 void discrete_device::discrete_build_list(const discrete_block *intf, sound_block_list_t &block_list)
488 {
489 	int node_count = 0;
490 
491 	for (; intf[node_count].type != DSS_NULL; )
492 	{
493 		/* scan imported */
494 		if (intf[node_count].type == DSO_IMPORT)
495 		{
496 			discrete_log("discrete_build_list() - DISCRETE_IMPORT @ NODE_%02d", NODE_INDEX(intf[node_count].node) );
497 			discrete_build_list((discrete_block *) intf[node_count].custom, block_list);
498 		}
499 		else if (intf[node_count].type == DSO_REPLACE)
500 		{
501 			bool found = false;
502 			node_count++;
503 			if (intf[node_count].type == DSS_NULL)
504 				fatalerror("discrete_build_list: DISCRETE_REPLACE at end of node_list\n");
505 
506 			for (int i=0; i < block_list.size(); i++)
507 			{
508 				const discrete_block *block = block_list[i];
509 
510 				if (block->type != NODE_SPECIAL )
511 					if (block->node == intf[node_count].node)
512 					{
513 						block_list[i] = &intf[node_count];
514 						discrete_log("discrete_build_list() - DISCRETE_REPLACE @ NODE_%02d", NODE_INDEX(intf[node_count].node) );
515 						found = true;
516 						break;
517 					}
518 			}
519 
520 			if (!found)
521 				fatalerror("discrete_build_list: DISCRETE_REPLACE did not found node %d\n", NODE_INDEX(intf[node_count].node));
522 
523 		}
524 		else if (intf[node_count].type == DSO_DELETE)
525 		{
526 			std::vector<int> deletethem;
527 
528 			for (int i=0; i<block_list.size(); i++)
529 			{
530 				const discrete_block *block = block_list[i];
531 
532 				if ((block->node >= intf[node_count].input_node[0]) &&
533 						(block->node <= intf[node_count].input_node[1]))
534 				{
535 					discrete_log("discrete_build_list() - DISCRETE_DELETE deleted NODE_%02d", NODE_INDEX(block->node) );
536 					deletethem.push_back(i);
537 				}
538 			}
539 			for (int i : deletethem)
540 				block_list.erase(block_list.begin() + i); // FIXME: how is this supposed to work if there's more than one item to remove?  indices are shifted back on each removal
541 		}
542 		else
543 		{
544 			discrete_log("discrete_build_list() - adding node %d\n", node_count);
545 			block_list.push_back(&intf[node_count]);
546 		}
547 
548 		node_count++;
549 	}
550 }
551 
552 //-------------------------------------------------
553 // discrete_sanity_check: Sanity check list
554 //-------------------------------------------------
555 
discrete_sanity_check(const sound_block_list_t & block_list)556 void discrete_device::discrete_sanity_check(const sound_block_list_t &block_list)
557 {
558 	int node_count = 0;
559 
560 	discrete_log("discrete_start() - Doing node list sanity check");
561 	for (int i=0; i < block_list.size(); i++)
562 	{
563 		const discrete_block *block = block_list[i];
564 
565 		/* make sure we don't have too many nodes overall */
566 		if (node_count > DISCRETE_MAX_NODES)
567 			fatalerror("discrete_start() - Upper limit of %d nodes exceeded, have you terminated the interface block?\n", DISCRETE_MAX_NODES);
568 
569 		/* make sure the node number is in range */
570 		if (block->node < NODE_START || block->node > NODE_END)
571 			fatalerror("discrete_start() - Invalid node number on node %02d descriptor\n", block->node);
572 
573 		/* make sure the node type is valid */
574 		if (block->type > DSO_OUTPUT)
575 			fatalerror("discrete_start() - Invalid function type on NODE_%02d\n", NODE_INDEX(block->node) );
576 
577 		/* make sure this is a main node */
578 		if (NODE_CHILD_NODE_NUM(block->node) > 0)
579 			fatalerror("discrete_start() - Child node number on NODE_%02d\n", NODE_INDEX(block->node) );
580 
581 		node_count++;
582 	}
583 	discrete_log("discrete_start() - Sanity check counted %d nodes", node_count);
584 
585 }
586 
587 //-------------------------------------------------
588 // discrete_sanity_check: Sanity check list
589 //-------------------------------------------------
590 
591 /*************************************
592  *
593  *  Master discrete system start
594  *
595  *************************************/
596 
597 
598 /*************************************
599  *
600  *  Master discrete system stop
601  *
602  *************************************/
603 
list_run_time(const discrete_device::node_list_t & list)604 static uint64_t list_run_time(const discrete_device::node_list_t &list)
605 {
606 	uint64_t total = 0;
607 
608 	for (const auto &node : list)
609 	{
610 		discrete_step_interface *step;
611 		if (node->interface(step))
612 			total += step->run_time;
613 	}
614 	return total;
615 }
616 
step_list_run_time(const discrete_device::node_step_list_t & list)617 static uint64_t step_list_run_time(const discrete_device::node_step_list_t &list)
618 {
619 	uint64_t total = 0;
620 
621 	for (discrete_step_interface *node : list)
622 	{
623 		total += node->run_time;
624 	}
625 	return total;
626 }
627 
display_profiling()628 void discrete_device::display_profiling()
629 {
630 	int count;
631 	uint64_t total;
632 	uint64_t tresh;
633 
634 	/* calculate total time */
635 	total = list_run_time(m_node_list);
636 	count = m_node_list.size();
637 	/* print statistics */
638 	osd_printf_info("Total Samples  : %16d\n", m_total_samples);
639 	tresh = total / count;
640 	osd_printf_info("Threshold (mean): %16d\n", tresh / m_total_samples);
641 	for (const auto &node : m_node_list)
642 	{
643 		discrete_step_interface *step;
644 		if (node->interface(step))
645 			if (step->run_time > tresh)
646 				osd_printf_info("%3d: %20s %8.2f %10.2f\n", node->index(), node->module_name(), double(step->run_time) / double(total) * 100.0, double(step->run_time) / double(m_total_samples));
647 	}
648 
649 	/* Task information */
650 	for (const auto &task : task_list)
651 	{
652 		double tt = step_list_run_time(task->step_list);
653 
654 		osd_printf_info("Task(%d): %8.2f %15.2f\n", task->task_group, tt / double(total) * 100.0, tt / double(m_total_samples));
655 	}
656 
657 	osd_printf_info("Average samples/double->update: %8.2f\n", double(m_total_samples) / double(m_total_stream_updates));
658 }
659 
660 
661 /*************************************
662  *
663  *  First pass init of nodes
664  *
665  *************************************/
666 
667 
init_nodes(const sound_block_list_t & block_list)668 void discrete_device::init_nodes(const sound_block_list_t &block_list)
669 {
670 	discrete_task *task = nullptr;
671 	/* list tail pointers */
672 	bool has_tasks = false;
673 
674 	/* check whether we have tasks ... */
675 	if (USE_DISCRETE_TASKS)
676 	{
677 		for (int i = 0; !has_tasks && (i < block_list.size()); i++)
678 		{
679 			if (block_list[i]->type == DSO_TASK_START)
680 				has_tasks = true;
681 		}
682 	}
683 
684 	if (!has_tasks)
685 	{
686 		/* make sure we have one simple task
687 		 * No need to create a node since there are no dependencies.
688 		 */
689 		task_list.push_back(std::make_unique<discrete_task>(*this));
690 		task = task_list.back().get();
691 	}
692 
693 	/* loop over all nodes */
694 	for (int i = 0; i < block_list.size(); i++)
695 	{
696 		const discrete_block &block = *block_list[i];
697 
698 		// add to node list
699 		m_node_list.push_back(block.factory(*this, block));
700 		discrete_base_node &node = *m_node_list.back();
701 
702 		if (block.node == NODE_SPECIAL)
703 		{
704 			// keep track of special nodes
705 			switch (block.type)
706 			{
707 				/* Output Node */
708 				case DSO_OUTPUT:
709 					/* nothing -> handled later */
710 					break;
711 
712 				/* CSVlog Node for debugging */
713 				case DSO_CSVLOG:
714 					break;
715 
716 				/* Wavelog Node for debugging */
717 				case DSO_WAVLOG:
718 					break;
719 
720 				/* Task processing */
721 				case DSO_TASK_START:
722 					if (USE_DISCRETE_TASKS)
723 					{
724 						if (task != nullptr)
725 							fatalerror("init_nodes() - Nested DISCRETE_START_TASK.\n");
726 						task_list.push_back(std::make_unique<discrete_task>(*this));
727 						task = task_list.back().get();
728 						task->task_group = block.initial[0];
729 						if (task->task_group < 0 || task->task_group >= DISCRETE_MAX_TASK_GROUPS)
730 							fatalerror("discrete_dso_task: illegal task_group %d\n", task->task_group);
731 						//logerror("task group %d\n", task->task_group);
732 					}
733 					break;
734 
735 				case DSO_TASK_END:
736 					if (USE_DISCRETE_TASKS)
737 					{
738 						if (task == nullptr)
739 							fatalerror("init_nodes() - NO DISCRETE_START_TASK.\n");
740 					}
741 					break;
742 
743 				default:
744 					fatalerror("init_nodes() - Failed, trying to create unknown special discrete node.\n");
745 			}
746 		}
747 		else
748 		{
749 			// otherwise, make sure we are not a duplicate, and put ourselves into the indexed list
750 			if (m_indexed_node[NODE_INDEX(block.node)])
751 				fatalerror("init_nodes() - Duplicate entries for NODE_%02d\n", NODE_INDEX(block.node));
752 			m_indexed_node[NODE_INDEX(block.node)] = &node;
753 		}
754 
755 		// our running order just follows the order specified
756 		// does the node step?
757 		discrete_step_interface *step;
758 		if (node.interface(step))
759 		{
760 			/* do we belong to a task? */
761 			if (task == nullptr)
762 				fatalerror("init_nodes() - found node outside of task: %s\n", node.module_name());
763 			else
764 				task->step_list.push_back(step);
765 		}
766 
767 		if (USE_DISCRETE_TASKS &&  block.type == DSO_TASK_END)
768 		{
769 			task = nullptr;
770 		}
771 
772 		/* and register save state */
773 		node.save_state();
774 	}
775 
776 	if (!has_tasks)
777 	{
778 	}
779 }
780 
781 
782 /*************************************
783  *
784  *  node_description implementation
785  *
786  *************************************/
787 
788 
same_module_index(const discrete_base_node & node)789 int discrete_device::same_module_index(const discrete_base_node &node)
790 {
791 	int index = 0;
792 
793 	for (const auto &n : m_node_list)
794 	{
795 		if (n.get() == &node)
796 			return index;
797 		if (n->module_type() == node.module_type())
798 			index++;
799 	}
800 	return -1;
801 }
802 
803 
804 //**************************************************************************
805 //  DEVICE CONFIGURATION
806 //**************************************************************************
807 
808 //-------------------------------------------------
809 //  discrete_device - constructor
810 //-------------------------------------------------
811 
discrete_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)812 discrete_device::discrete_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
813 	: device_t(mconfig, type, tag, owner, clock),
814 		m_intf(nullptr),
815 		m_sample_rate(0),
816 		m_sample_time(0),
817 		m_neg_sample_time(0),
818 		m_indexed_node(nullptr),
819 		m_disclogfile(nullptr),
820 		m_queue(nullptr),
821 		m_profiling(0),
822 		m_total_samples(0),
823 		m_total_stream_updates(0)
824 {
825 }
826 
discrete_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)827 discrete_sound_device::discrete_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
828 	: discrete_device(mconfig, DISCRETE, tag, owner, clock),
829 		device_sound_interface(mconfig, *this),
830 		m_stream(nullptr)
831 {
832 }
833 
~discrete_device()834 discrete_device::~discrete_device()
835 {
836 }
837 
838 //-------------------------------------------------
839 //  device_start - device-specific startup
840 //-------------------------------------------------
841 
device_start()842 void discrete_device::device_start()
843 {
844 	// create the stream
845 	//m_stream = stream_alloc(0, 2, 22257);
846 
847 	const discrete_block *intf_start = m_intf;
848 
849 	/* If a clock is specified we will use it, otherwise run at the audio sample rate. */
850 	if (this->clock())
851 		m_sample_rate = this->clock();
852 	else
853 		m_sample_rate = this->machine().sample_rate();
854 	m_sample_time = 1.0 / m_sample_rate;
855 	m_neg_sample_time = - m_sample_time;
856 
857 	m_total_samples = 0;
858 	m_total_stream_updates = 0;
859 
860 	/* create the logfile */
861 	if (DISCRETE_DEBUGLOG)
862 		m_disclogfile = fopen(util::string_format("discrete%s.log", this->tag()).c_str(), "w");
863 
864 	/* enable profiling */
865 	m_profiling = 0;
866 	if (osd_getenv("DISCRETE_PROFILING"))
867 		m_profiling = atoi(osd_getenv("DISCRETE_PROFILING"));
868 
869 	/* Build the final block list */
870 	sound_block_list_t block_list;
871 	discrete_build_list(intf_start, block_list);
872 
873 	/* first pass through the nodes: sanity check, fill in the indexed_nodes, and make a total count */
874 	discrete_sanity_check(block_list);
875 
876 	/* Start with empty lists */
877 	m_node_list.clear();
878 
879 	/* allocate memory to hold pointers to nodes by index */
880 	m_indexed_node = make_unique_clear<discrete_base_node * []>(DISCRETE_MAX_NODES);
881 
882 	/* initialize the node data */
883 	init_nodes(block_list);
884 
885 	/* now go back and find pointers to all input nodes */
886 	for (const auto &node : m_node_list)
887 	{
888 		node->resolve_input_nodes();
889 	}
890 
891 	/* allocate a queue */
892 	m_queue = osd_work_queue_alloc(WORK_QUEUE_FLAG_MULTI | WORK_QUEUE_FLAG_HIGH_FREQ);
893 
894 	/* Process nodes which have a start func */
895 	for (const auto &node : m_node_list)
896 	{
897 		node->start();
898 	}
899 
900 	/* Now set up tasks */
901 	for (const auto &task : task_list)
902 	{
903 		for (const auto &dest_task : task_list)
904 		{
905 			if (task->task_group > dest_task->task_group)
906 				dest_task->check(*task);
907 		}
908 	}
909 }
910 
device_stop()911 void discrete_device::device_stop()
912 {
913 	if (m_queue)
914 	{
915 		osd_work_queue_free(m_queue);
916 	}
917 
918 	if (m_profiling)
919 	{
920 		display_profiling();
921 	}
922 
923 	/* Process nodes which have a stop func */
924 
925 	for (const auto &node : m_node_list)
926 	{
927 		node->stop();
928 	}
929 
930 	if (DISCRETE_DEBUGLOG)
931 	{
932 		/* close the debug log */
933 		if (m_disclogfile)
934 			fclose(m_disclogfile);
935 		m_disclogfile = nullptr;
936 	}
937 }
938 
939 //-------------------------------------------------
940 //  device_start - device-specific startup
941 //-------------------------------------------------
942 
device_start()943 void discrete_sound_device::device_start()
944 {
945 	m_input_stream_list.clear();
946 	m_output_list.clear();
947 
948 	/* call the parent */
949 	discrete_device::device_start();
950 
951 	/* look for input stream nodes */
952 	for (const auto &node : m_node_list)
953 	{
954 		/* if we are an stream input node, track that */
955 		discrete_dss_input_stream_node *input_stream = dynamic_cast<discrete_dss_input_stream_node *>(node.get());
956 		if (input_stream != nullptr)
957 		{
958 			m_input_stream_list.push_back(input_stream);
959 		}
960 		/* if this is an output interface, add it the output list */
961 		discrete_sound_output_interface *out;
962 		if (node->interface(out))
963 			m_output_list.push_back(out);
964 	}
965 
966 	/* if no outputs, give an error */
967 	if (m_output_list.empty())
968 		fatalerror("init_nodes() - Couldn't find an output node\n");
969 
970 	/* initialize the stream(s) */
971 	m_stream = stream_alloc(m_input_stream_list.size(), m_output_list.size(), m_sample_rate);
972 
973 	/* Finalize stream_input_nodes */
974 	for (discrete_dss_input_stream_node *node : m_input_stream_list)
975 	{
976 		node->stream_start();
977 	}
978 
979 
980 }
981 
982 //-------------------------------------------------
983 //  device_reset - device-specific reset
984 //-------------------------------------------------
985 
device_reset()986 void discrete_device::device_reset()
987 {
988 	update_to_current_time();
989 
990 	/* loop over all nodes */
991 	for (const auto &node : m_node_list)
992 	{
993 		/* Fimxe : node_level */
994 		node->m_output[0] = 0;
995 
996 		node->reset();
997 	}
998 }
999 
device_reset()1000 void discrete_sound_device::device_reset()
1001 {
1002 	discrete_device::device_reset();
1003 }
1004 
1005 //-------------------------------------------------
1006 //  discrete_device_process - process a number of
1007 //  samples.
1008 //
1009 //  input / output buffers are s32
1010 //  to not to have to convert the buffers.
1011 //  a "discrete cpu" device will pass nullptr here
1012 //-------------------------------------------------
1013 
process(int samples)1014 void discrete_device::process(int samples)
1015 {
1016 	if (samples == 0)
1017 		return;
1018 
1019 	/* Setup tasks */
1020 	for (const auto &task : task_list)
1021 	{
1022 		/* unlock the thread */
1023 		task->unlock();
1024 
1025 		task->prepare_for_queue(samples);
1026 	}
1027 
1028 	for (const auto &task : task_list)
1029 	{
1030 		/* Fire a work item for each task */
1031 		(void)task;
1032 		osd_work_item_queue(m_queue, discrete_task::task_callback, (void *)&task_list, WORK_ITEM_FLAG_AUTO_RELEASE);
1033 	}
1034 	osd_work_queue_wait(m_queue, osd_ticks_per_second()*10);
1035 
1036 	if (m_profiling)
1037 	{
1038 		m_total_samples += samples;
1039 		m_total_stream_updates++;
1040 	}
1041 }
1042 
1043 //-------------------------------------------------
1044 //  sound_stream_update - handle update requests for
1045 //  our sound stream
1046 //-------------------------------------------------
1047 
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)1048 void discrete_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
1049 {
1050 	int outputnum = 0;
1051 
1052 	/* Setup any output streams */
1053 	for (discrete_sound_output_interface *node : m_output_list)
1054 	{
1055 		node->set_output_ptr(outputs[outputnum]);
1056 		outputnum++;
1057 	}
1058 
1059 	/* Setup any input streams */
1060 	for (discrete_dss_input_stream_node *node : m_input_stream_list)
1061 	{
1062 		node->m_inview = &inputs[node->m_stream_in_number];
1063 		node->m_inview_sample = 0;
1064 	}
1065 
1066 	/* just process it */
1067 	process(outputs[0].samples());
1068 }
1069 
1070 //-------------------------------------------------
1071 //  read - read from the chip's registers and internal RAM
1072 //-------------------------------------------------
1073 
read(offs_t offset)1074 uint8_t discrete_device::read(offs_t offset)
1075 {
1076 	const discrete_base_node *node = discrete_find_node(offset);
1077 
1078 	uint8_t data;
1079 
1080 	/* Read the node input value if allowed */
1081 	if (node)
1082 	{
1083 		/* Bring the system up to now */
1084 		update_to_current_time();
1085 
1086 		data = (uint8_t) node->m_output[NODE_CHILD_NODE_NUM(offset)];
1087 	}
1088 	else
1089 		fatalerror("discrete_sound_r read from non-existent NODE_%02d\n", offset-NODE_00);
1090 
1091 	return data;
1092 }
1093 
1094 //-------------------------------------------------
1095 //  write - write to the chip's registers and internal RAM
1096 //-------------------------------------------------
1097 
write(offs_t offset,uint8_t data)1098 void discrete_device::write(offs_t offset, uint8_t data)
1099 {
1100 	const discrete_base_node *node = discrete_find_node(offset);
1101 
1102 	/* Update the node input value if it's a proper input node */
1103 	if (node)
1104 	{
1105 		discrete_input_interface *intf;
1106 		if (node->interface(intf))
1107 				intf->input_write(0, data);
1108 		else
1109 			discrete_log("discrete_sound_w write to non-input NODE_%02d\n", offset-NODE_00);
1110 	}
1111 	else
1112 	{
1113 		discrete_log("discrete_sound_w write to non-existent NODE_%02d\n", offset-NODE_00);
1114 	}
1115 }
1116