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