Mercurial > repos > blastem
comparison mediaplayer.c @ 2482:fb8f49b0aece
Impelement partial support for DAC stream commands in VGM player
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 30 Mar 2024 21:10:20 -0700 |
parents | 9da3de58410d |
children | 6c834c281fa2 |
comparison
equal
deleted
inserted
replaced
2481:f0645adddf0d | 2482:fb8f49b0aece |
---|---|
19 AUDIO_WAVE, | 19 AUDIO_WAVE, |
20 AUDIO_FLAC, | 20 AUDIO_FLAC, |
21 MEDIA_UNKNOWN | 21 MEDIA_UNKNOWN |
22 }; | 22 }; |
23 | 23 |
24 #define STREAM_FLAG_REVERSE 0x01 | |
25 #define STREAM_FLAG_LOOP 0x10 | |
26 #define STREAM_FLAG_PLAY 0x80 | |
27 | |
24 uint32_t cycles_to_samples(uint32_t clock_rate, uint32_t cycles) | 28 uint32_t cycles_to_samples(uint32_t clock_rate, uint32_t cycles) |
25 { | 29 { |
26 return ((uint64_t)cycles) * ((uint64_t)44100) / ((uint64_t)clock_rate); | 30 return ((uint64_t)cycles) * ((uint64_t)44100) / ((uint64_t)clock_rate); |
27 } | 31 } |
28 | 32 |
48 | 52 |
49 void ym_no_scope(void *context) | 53 void ym_no_scope(void *context) |
50 { | 54 { |
51 ym2612_context *ym = context; | 55 ym2612_context *ym = context; |
52 ym->scope = NULL; | 56 ym->scope = NULL; |
57 } | |
58 | |
59 void ym_stream(chip_info *chip, uint8_t port, uint8_t command, uint16_t sample) | |
60 { | |
61 ym2612_context *ym = chip->context; | |
62 if (port) { | |
63 ym_address_write_part2(ym, command); | |
64 } else { | |
65 ym_address_write_part1(ym, command); | |
66 } | |
67 ym_data_write(ym, sample); | |
53 } | 68 } |
54 | 69 |
55 void psg_adjust(chip_info *chip) | 70 void psg_adjust(chip_info *chip) |
56 { | 71 { |
57 psg_context *psg = chip->context; | 72 psg_context *psg = chip->context; |
123 while (samples > MAX_RUN_SAMPLES) | 138 while (samples > MAX_RUN_SAMPLES) |
124 { | 139 { |
125 vgm_wait(player, MAX_RUN_SAMPLES); | 140 vgm_wait(player, MAX_RUN_SAMPLES); |
126 samples -= MAX_RUN_SAMPLES; | 141 samples -= MAX_RUN_SAMPLES; |
127 } | 142 } |
143 uint8_t keep_going; | |
144 do { | |
145 keep_going = 0; | |
146 for (uint32_t i = 0; i < 0xFF; i++) | |
147 { | |
148 if (!player->streams[i] || !(player->streams[i]->flags & STREAM_FLAG_PLAY)) { | |
149 continue; | |
150 } | |
151 vgm_stream *stream = player->streams[i]; | |
152 uint32_t cycle = samples_to_cycles(stream->chip->clock, stream->chip->samples + samples); | |
153 if (cycle >= stream->next_sample_cycle) { | |
154 stream->chip->run(stream->chip->context, stream->next_sample_cycle); | |
155 if (stream->block_offset >= stream->cur_block->size) { | |
156 stream->cur_block = stream->cur_block->next; | |
157 if (!stream->cur_block) { | |
158 //TODO: looping support | |
159 player->streams[i]->flags &= ~STREAM_FLAG_PLAY; | |
160 continue; | |
161 } | |
162 } | |
163 //TODO: support for chips where the stream data unit is not a byte | |
164 uint16_t sample = stream->cur_block->data[stream->block_offset]; | |
165 stream->block_offset += stream->step; | |
166 if (stream->chip->stream) { | |
167 stream->chip->stream(stream->chip, stream->port, stream->command, sample); | |
168 } | |
169 stream->next_sample_cycle += stream->cycles_per_sample; | |
170 //TODO: deal with cycle adjustments | |
171 keep_going = 1; | |
172 } | |
173 } | |
174 } while (keep_going); | |
128 for (uint32_t i = 0; i < num_chips; i++) | 175 for (uint32_t i = 0; i < num_chips; i++) |
129 { | 176 { |
130 chips[i].samples += samples; | 177 chips[i].samples += samples; |
131 chips[i].run(chips[i].context, samples_to_cycles(chips[i].clock, chips[i].samples)); | 178 chips[i].run(chips[i].context, samples_to_cycles(chips[i].clock, chips[i].samples)); |
132 chips[i].adjust(chips + i); | 179 chips[i].adjust(chips + i); |
161 chip_info *find_chip_by_data(media_player *player, uint8_t data_type) | 208 chip_info *find_chip_by_data(media_player *player, uint8_t data_type) |
162 { | 209 { |
163 for (uint32_t i = 0; i < player->num_chips; i++) | 210 for (uint32_t i = 0; i < player->num_chips; i++) |
164 { | 211 { |
165 if (player->chips[i].data_type == data_type) { | 212 if (player->chips[i].data_type == data_type) { |
213 return &player->chips[i]; | |
214 } | |
215 } | |
216 return NULL; | |
217 } | |
218 | |
219 chip_info *find_chip_by_stream(media_player *player, uint8_t stream_type) | |
220 { | |
221 for (uint32_t i = 0; i < player->num_chips; i++) | |
222 { | |
223 if (player->chips[i].stream_type == stream_type) { | |
166 return &player->chips[i]; | 224 return &player->chips[i]; |
167 } | 225 } |
168 } | 226 } |
169 return NULL; | 227 return NULL; |
170 } | 228 } |
398 (*cur)->data = ((uint8_t *)player->media->buffer) + player->current_offset; | 456 (*cur)->data = ((uint8_t *)player->media->buffer) + player->current_offset; |
399 } else { | 457 } else { |
400 fprintf(stderr, "Skipping data block with unrecognized type %X\n", data_type); | 458 fprintf(stderr, "Skipping data block with unrecognized type %X\n", data_type); |
401 } | 459 } |
402 player->current_offset += data_size; | 460 player->current_offset += data_size; |
461 } | |
462 break; | |
463 case CMD_DAC_STREAM_SETUP: | |
464 if (player->current_offset > player->media->size - 4) { | |
465 vgm_stop(player); | |
466 goto frame_end; | |
467 } else { | |
468 uint8_t stream_id = read_byte(player); | |
469 if (!player->streams[stream_id]) { | |
470 player->streams[stream_id] = calloc(1, sizeof(vgm_stream)); | |
471 } | |
472 vgm_stream *stream = player->streams[stream_id]; | |
473 uint8_t chip_type = read_byte(player); | |
474 stream->chip = find_chip_by_stream(player, chip_type); | |
475 if (!stream->chip) { | |
476 fprintf(stderr, "Failed to find chip with stream type %d for stream %d\n", chip_type, stream_id); | |
477 } | |
478 stream->port = read_byte(player); | |
479 stream->command = read_byte(player); | |
480 } | |
481 break; | |
482 case CMD_DAC_STREAM_DATA: | |
483 if (player->current_offset > player->media->size - 4) { | |
484 vgm_stop(player); | |
485 goto frame_end; | |
486 } else { | |
487 uint8_t stream_id = read_byte(player); | |
488 if (!player->streams[stream_id]) { | |
489 player->streams[stream_id] = calloc(1, sizeof(vgm_stream)); | |
490 } | |
491 vgm_stream *stream = player->streams[stream_id]; | |
492 stream->data_type = read_byte(player); | |
493 stream->step = read_byte(player); | |
494 stream->start_offset = read_byte(player); | |
495 } | |
496 break; | |
497 case CMD_DAC_STREAM_FREQ: | |
498 if (player->current_offset > player->media->size - 4) { | |
499 vgm_stop(player); | |
500 goto frame_end; | |
501 } else { | |
502 uint8_t stream_id = read_byte(player); | |
503 if (!player->streams[stream_id]) { | |
504 player->streams[stream_id] = calloc(1, sizeof(vgm_stream)); | |
505 } | |
506 vgm_stream *stream = player->streams[stream_id]; | |
507 stream->sample_rate = read_long_le(player); | |
508 if (stream->chip) { | |
509 stream->cycles_per_sample = stream->chip->clock / stream->sample_rate; | |
510 } | |
511 } | |
512 break; | |
513 case CMD_DAC_STREAM_START: | |
514 if (player->current_offset > player->media->size - 10) { | |
515 vgm_stop(player); | |
516 goto frame_end; | |
517 } else { | |
518 uint8_t stream_id = read_byte(player); | |
519 if (!player->streams[stream_id]) { | |
520 player->streams[stream_id] = calloc(1, sizeof(vgm_stream)); | |
521 } | |
522 vgm_stream *stream = player->streams[stream_id]; | |
523 uint32_t data_start_offset = read_long_le(player); | |
524 uint8_t length_mode = read_byte(player); | |
525 uint32_t length = read_long_le(player); | |
526 stream->flags = length_mode & STREAM_FLAG_REVERSE; | |
527 if (length_mode & 0x80) { | |
528 stream->flags |= STREAM_FLAG_LOOP; | |
529 } | |
530 length_mode &= 0x3; | |
531 if (length_mode) { | |
532 stream->length_mode = length_mode; | |
533 if (length_mode == 2) { | |
534 //length is in msec | |
535 if (stream->chip) { | |
536 stream->remaining = (((uint64_t)length) * (uint64_t)stream->sample_rate) / 1000; | |
537 } | |
538 } else if (length_mode == 3) { | |
539 //play until end of data | |
540 stream->remaining = 0xFFFFFFFF; | |
541 } else { | |
542 //length is in commands? | |
543 stream->remaining = length; | |
544 } | |
545 } | |
546 if (stream->chip && data_start_offset != 0xFFFFFFFF) { | |
547 data_block * cur_block = stream->chip->blocks; | |
548 data_start_offset += stream->start_offset; | |
549 while (cur_block) | |
550 { | |
551 if (cur_block->type == stream->data_type) { | |
552 if (data_start_offset >= cur_block->size) { | |
553 data_start_offset -= cur_block->size; | |
554 cur_block = cur_block->next; | |
555 } else { | |
556 stream->block_offset = data_start_offset; | |
557 stream->cur_block = cur_block; | |
558 break; | |
559 } | |
560 } else { | |
561 cur_block = cur_block->next; | |
562 } | |
563 } | |
564 } | |
565 if (stream->chip && stream->cur_block) { | |
566 stream->flags |= STREAM_FLAG_PLAY; | |
567 } | |
568 } | |
569 break; | |
570 case CMD_DAC_STREAM_STOP: | |
571 if (player->current_offset < player->media->size) { | |
572 vgm_stop(player); | |
573 goto frame_end; | |
574 } else { | |
575 uint8_t stream_id = read_byte(player); | |
576 if (stream_id == 0xFF) { | |
577 for (uint32_t i = 0; i < 0xFF; i++) { | |
578 if (player->streams[i]) { | |
579 player->streams[i]->flags &= ~STREAM_FLAG_PLAY; | |
580 } | |
581 } | |
582 } else { | |
583 if (!player->streams[stream_id]) { | |
584 player->streams[stream_id] = calloc(1, sizeof(vgm_stream)); | |
585 } | |
586 player->streams[stream_id]->flags &= ~STREAM_FLAG_PLAY; | |
587 } | |
588 } | |
589 break; | |
590 case CMD_DAC_STREAM_STARTFAST: | |
591 if (player->current_offset > player->media->size - 4) { | |
592 vgm_stop(player); | |
593 goto frame_end; | |
594 } else { | |
595 uint8_t stream_id = read_byte(player); | |
596 if (!player->streams[stream_id]) { | |
597 player->streams[stream_id] = calloc(1, sizeof(vgm_stream)); | |
598 } | |
599 vgm_stream *stream = player->streams[stream_id]; | |
600 uint16_t block_id = read_word_le(player); | |
601 stream->flags = read_byte(player) & (STREAM_FLAG_LOOP|STREAM_FLAG_REVERSE); | |
602 if (stream->chip) { | |
603 uint16_t cur_block_id = 0; | |
604 data_block *cur_block = stream->chip->blocks; | |
605 while (cur_block) | |
606 { | |
607 if (cur_block_id == block_id) { | |
608 stream->cur_block = cur_block; | |
609 stream->block_offset = stream->start_offset; | |
610 break; | |
611 } | |
612 if (cur_block->type == stream->data_type) { | |
613 cur_block_id++; | |
614 } | |
615 cur_block = cur_block->next; | |
616 } | |
617 if (stream->cur_block) { | |
618 stream->flags |= STREAM_FLAG_PLAY; | |
619 stream->next_sample_cycle = samples_to_cycles(stream->chip->clock, stream->chip->samples); | |
620 } | |
621 } | |
403 } | 622 } |
404 break; | 623 break; |
405 case CMD_DATA_SEEK: | 624 case CMD_DATA_SEEK: |
406 if (player->current_offset > player->media->size - 4) { | 625 if (player->current_offset > player->media->size - 4) { |
407 vgm_stop(player); | 626 vgm_stop(player); |
556 .run = (chip_run_fun)ym_run, | 775 .run = (chip_run_fun)ym_run, |
557 .adjust = ym_adjust, | 776 .adjust = ym_adjust, |
558 .scope = ym_scope, | 777 .scope = ym_scope, |
559 .no_scope = ym_no_scope, | 778 .no_scope = ym_no_scope, |
560 .free = (chip_noarg_fun)ym_free, | 779 .free = (chip_noarg_fun)ym_free, |
780 .stream = ym_stream, | |
561 .clock = player->vgm->ym2612_clk, | 781 .clock = player->vgm->ym2612_clk, |
562 .samples = 0, | 782 .samples = 0, |
563 .cmd = CMD_YM2612_0, | 783 .cmd = CMD_YM2612_0, |
564 .data_type = DATA_YM2612_PCM | 784 .data_type = DATA_YM2612_PCM, |
785 .stream_type = STREAM_CHIP_YM2612 | |
565 }; | 786 }; |
566 } | 787 } |
567 if (player->vgm->sn76489_clk) { | 788 if (player->vgm->sn76489_clk) { |
568 psg_context *psg = calloc(1, sizeof(psg_context)); | 789 psg_context *psg = calloc(1, sizeof(psg_context)); |
569 psg_init(psg, player->vgm->sn76489_clk, 16); | 790 psg_init(psg, player->vgm->sn76489_clk, 16); |
575 .no_scope = ym_no_scope, | 796 .no_scope = ym_no_scope, |
576 .free = (chip_noarg_fun)psg_free, | 797 .free = (chip_noarg_fun)psg_free, |
577 .clock = player->vgm->sn76489_clk, | 798 .clock = player->vgm->sn76489_clk, |
578 .samples = 0, | 799 .samples = 0, |
579 .cmd = CMD_PSG, | 800 .cmd = CMD_PSG, |
580 .data_type = 0xFF | 801 .data_type = 0xFF, |
802 .stream_type = STREAM_CHIP_PSG | |
581 }; | 803 }; |
582 } | 804 } |
583 if (player->vgm_ext && player->vgm_ext->rf5c68_clk) { | 805 if (player->vgm_ext && player->vgm_ext->rf5c68_clk) { |
584 rf5c164 *pcm = calloc(1, sizeof(rf5c164)); | 806 rf5c164 *pcm = calloc(1, sizeof(rf5c164)); |
585 rf5c164_init(pcm, player->vgm_ext->rf5c68_clk, 1); | 807 rf5c164_init(pcm, player->vgm_ext->rf5c68_clk, 1); |
591 .no_scope = pcm_no_scope, | 813 .no_scope = pcm_no_scope, |
592 .free = pcm_free, | 814 .free = pcm_free, |
593 .clock = player->vgm_ext->rf5c68_clk, | 815 .clock = player->vgm_ext->rf5c68_clk, |
594 .samples = 0, | 816 .samples = 0, |
595 .cmd = CMD_PCM68_REG, | 817 .cmd = CMD_PCM68_REG, |
596 .data_type = DATA_RF5C68 | 818 .data_type = DATA_RF5C68, |
819 .stream_type = STREAM_CHIP_RF5C68 | |
597 }; | 820 }; |
598 } | 821 } |
599 if (player->vgm_ext && player->vgm_ext->rf5c164_clk) { | 822 if (player->vgm_ext && player->vgm_ext->rf5c164_clk) { |
600 rf5c164 *pcm = calloc(1, sizeof(rf5c164)); | 823 rf5c164 *pcm = calloc(1, sizeof(rf5c164)); |
601 rf5c164_init(pcm, player->vgm_ext->rf5c164_clk, 1); | 824 rf5c164_init(pcm, player->vgm_ext->rf5c164_clk, 1); |
607 .no_scope = pcm_no_scope, | 830 .no_scope = pcm_no_scope, |
608 .free = pcm_free, | 831 .free = pcm_free, |
609 .clock = player->vgm_ext->rf5c164_clk, | 832 .clock = player->vgm_ext->rf5c164_clk, |
610 .samples = 0, | 833 .samples = 0, |
611 .cmd = CMD_PCM164_REG, | 834 .cmd = CMD_PCM164_REG, |
612 .data_type = DATA_RF5C164 | 835 .data_type = DATA_RF5C164, |
836 .stream_type = STREAM_CHIP_RF5C164 | |
613 }; | 837 }; |
614 } | 838 } |
615 player->current_offset = player->vgm->data_offset + offsetof(vgm_header, data_offset); | 839 player->current_offset = player->vgm->data_offset + offsetof(vgm_header, data_offset); |
616 player->loop_count = 2; | 840 player->loop_count = 2; |
617 } | 841 } |