├── .clang-format ├── .gitattributes ├── LICENSE ├── PROJ ├── compile.bat ├── linker ├── main.c ├── makefile ├── sdrv.bin └── srcs ├── README.md ├── documentation.md ├── jo_cdda_demo ├── cd │ ├── ABS.TXT │ ├── BIB.TXT │ ├── CPY.TXT │ ├── Nbgm.ADX │ ├── def_flag.PCM │ ├── hav_flag.PCM │ └── sdrv.bin ├── clean.bat ├── clean.sh ├── compile.bat ├── compile.sh ├── end_times.wav ├── main.c ├── makefile ├── pcmsys.c ├── pcmsys.h ├── run_with_daemon_tools_and_ssf.bat ├── run_with_mednafen.bat ├── run_with_nova.bat ├── run_with_powershell_and_ssf.ps1 ├── run_with_virtual_clone_drive_and_ssf.bat ├── run_with_yabaSanshiro.bat ├── run_with_yabause.bat ├── run_with_yabause.sh └── use_mednafen.bat ├── jo_demo ├── cd │ ├── ABS.TXT │ ├── BIB.TXT │ ├── CPY.TXT │ ├── Nbgm.ADX │ ├── def_flag.PCM │ ├── hav_flag.PCM │ └── sdrv.bin ├── clean.bat ├── clean.sh ├── compile.bat ├── compile.sh ├── main.c ├── makefile ├── pcmsys.c ├── pcmsys.h ├── run_with_daemon_tools_and_ssf.bat ├── run_with_mednafen.bat ├── run_with_nova.bat ├── run_with_powershell_and_ssf.ps1 ├── run_with_virtual_clone_drive_and_ssf.bat ├── run_with_yabaSanshiro.bat ├── run_with_yabause.bat ├── run_with_yabause.sh └── use_mednafen.bat ├── jo_stream_demo ├── cd │ ├── ABS.TXT │ ├── BIB.TXT │ ├── CPY.TXT │ ├── TSTF.TXT │ ├── animes.ADX │ ├── exert.PCM │ ├── gordon.ADX │ ├── mgear.PCM │ └── sdrv.bin ├── clean.bat ├── compile.bat ├── main.c ├── makefile ├── pcmstm.c ├── pcmstm.h ├── pcmsys.c ├── pcmsys.h ├── run_with_daemon_tools_and_ssf.bat ├── run_with_mednafen.bat ├── run_with_nova.bat ├── run_with_powershell_and_ssf.ps1 ├── run_with_virtual_clone_drive_and_ssf.bat ├── run_with_yabaSanshiro.bat ├── run_with_yabause.bat ├── ssf.bat └── use_mednafen.bat ├── logo_by_vbt_toaster2000.png ├── m68k-elf ├── ar.exe ├── as.exe ├── cc1.exe ├── cc1plus.exe ├── collect2.exe ├── g++-mapper-server.exe ├── iconv.dll ├── ld.bfd.exe ├── ld.exe ├── liblto_plugin.dll ├── liblto_plugin.dll.a ├── liblto_plugin.la ├── libwinpthread-1.dll ├── lto-wrapper.exe ├── lto1.exe ├── m68k-elf-addr2line.exe ├── m68k-elf-ar.exe ├── m68k-elf-as.exe ├── m68k-elf-c++.exe ├── m68k-elf-c++filt.exe ├── m68k-elf-cpp.exe ├── m68k-elf-elfedit.exe ├── m68k-elf-g++.exe ├── m68k-elf-gcc-11.1.0.exe ├── m68k-elf-gcc-ar.exe ├── m68k-elf-gcc-nm.exe ├── m68k-elf-gcc-ranlib.exe ├── m68k-elf-gcc.exe ├── m68k-elf-gcov-dump.exe ├── m68k-elf-gcov-tool.exe ├── m68k-elf-gcov.exe ├── m68k-elf-gdb-add-index ├── m68k-elf-gdb.exe ├── m68k-elf-gprof.exe ├── m68k-elf-ld.bfd.exe ├── m68k-elf-ld.exe ├── m68k-elf-lto-dump.exe ├── m68k-elf-nm.exe ├── m68k-elf-objcopy.exe ├── m68k-elf-objdump.exe ├── m68k-elf-ranlib.exe ├── m68k-elf-readelf.exe ├── m68k-elf-size.exe ├── m68k-elf-strings.exe ├── m68k-elf-strip.exe ├── make.exe ├── nm.exe ├── objcopy.exe ├── objdump.exe ├── ranlib.exe ├── readelf.exe └── strip.exe └── small.png /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: LLVM 3 | AlignAfterOpenBracket: DontAlign 4 | AlignConsecutiveMacros: 'true' 5 | AlignConsecutiveAssignments: 'false' 6 | AlignConsecutiveDeclarations: 'false' 7 | AlignEscapedNewlines: Left 8 | AlignOperands: 'false' 9 | AlignTrailingComments: 'true' 10 | AllowAllArgumentsOnNextLine: 'false' 11 | AllowAllConstructorInitializersOnNextLine: 'false' 12 | AllowAllParametersOfDeclarationOnNextLine: 'false' 13 | AllowShortBlocksOnASingleLine: 'true' 14 | AllowShortCaseLabelsOnASingleLine: 'false' 15 | AllowShortFunctionsOnASingleLine: All 16 | AllowShortIfStatementsOnASingleLine: Never 17 | AllowShortLambdasOnASingleLine: All 18 | AllowShortLoopsOnASingleLine: 'false' 19 | AlwaysBreakAfterDefinitionReturnType: None 20 | AlwaysBreakAfterReturnType: None 21 | AlwaysBreakBeforeMultilineStrings: 'false' 22 | AlwaysBreakTemplateDeclarations: 'Yes' 23 | BinPackArguments: 'true' 24 | BinPackParameters: 'true' 25 | BreakBeforeBinaryOperators: None 26 | BreakBeforeBraces: Attach 27 | BreakBeforeTernaryOperators: 'false' 28 | BreakConstructorInitializers: BeforeComma 29 | BreakStringLiterals: 'false' 30 | ColumnLimit: '120' 31 | CompactNamespaces: 'false' 32 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 33 | ContinuationIndentWidth: '2' 34 | Cpp11BracedListStyle: 'true' 35 | FixNamespaceComments: 'true' 36 | IncludeBlocks: Regroup 37 | IndentCaseLabels: 'false' 38 | IndentPPDirectives: AfterHash 39 | IndentWidth: '2' 40 | IndentWrappedFunctionNames: 'false' 41 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 42 | Language: Cpp 43 | NamespaceIndentation: None 44 | PenaltyBreakBeforeFirstCallParameter: '9999' 45 | PenaltyBreakFirstLessLess: '9999' 46 | PenaltyReturnTypeOnItsOwnLine: '9999' 47 | PointerAlignment: Right 48 | ReflowComments: 'true' 49 | SortIncludes: 'false' 50 | SpaceAfterCStyleCast: 'true' 51 | SpaceBeforeAssignmentOperators: 'true' 52 | SpaceBeforeCpp11BracedList: 'false' 53 | SpaceBeforeCtorInitializerColon: 'true' 54 | SpaceBeforeInheritanceColon: 'true' 55 | SpaceBeforeRangeBasedForLoopColon: 'true' 56 | Standard: Cpp11 57 | TabWidth: '2' 58 | UseTab: Never 59 | 60 | ... 61 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ponut64 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PROJ/compile.bat: -------------------------------------------------------------------------------- 1 | SET COMPILER_DIR=D:\68k\m68k-elf 2 | SET PATH=%COMPILER_DIR%;%cd% 3 | make -f D:\68k\PROJ\makefile srcs 4 | PAUSE -------------------------------------------------------------------------------- /PROJ/linker: -------------------------------------------------------------------------------- 1 | __DYNAMIC = 0; 2 | 3 | MEMORY 4 | { 5 | ram1 (rwx) : ORIGIN = 0x0, LENGTH = 4K /* vector table */ 6 | ram2 (rwx) : ORIGIN = 0x408, LENGTH = 510K 7 | } 8 | 9 | /* Set Stack to end of memory */ 10 | PROVIDE (__stack = 0x80000 - 1K); 11 | 12 | /* 13 | * Initalize some symbols to be zero so we can reference them in the 14 | * crt0 without core dumping. These functions are all optional, but 15 | * we do this so we can have our crt0 always use them if they exist. 16 | * This is so BSPs work better when using the crt0 installed with gcc. 17 | * We have to initalize them twice, so we cover a.out (which prepends 18 | * an underscore) and coff object file formats. 19 | */ 20 | PROVIDE (hardware_init_hook = 0); 21 | PROVIDE (_hardware_init_hook = 0); 22 | PROVIDE (software_init_hook = 0); 23 | PROVIDE (_software_init_hook = 0); 24 | /* 25 | * stick everything in ram (of course) 26 | */ 27 | SECTIONS 28 | { 29 | /* .vecs : { vectors.obj } > ram1 */ 30 | VECTORS : { 31 | __vectors = .; 32 | *(VECTORS) 33 | } > ram1 34 | .text : 35 | { 36 | *(.text) 37 | . = ALIGN(0x4); 38 | __CTOR_LIST__ = .; 39 | ___CTOR_LIST__ = .; 40 | LONG((__CTOR_END__ - __CTOR_LIST__) / 4 - 2) 41 | *(.ctors) 42 | LONG(0) 43 | __CTOR_END__ = .; 44 | __DTOR_LIST__ = .; 45 | ___DTOR_LIST__ = .; 46 | LONG((__DTOR_END__ - __DTOR_LIST__) / 4 - 2) 47 | *(.dtors) 48 | LONG(0) 49 | __DTOR_END__ = .; 50 | *(.rodata) 51 | *(.gcc_except_table) 52 | 53 | __INIT_SECTION__ = . ; 54 | LONG (0x4e560000) /* linkw %fp,#0 */ 55 | *(.init) 56 | SHORT (0x4e5e) /* unlk %fp */ 57 | SHORT (0x4e75) /* rts */ 58 | 59 | __FINI_SECTION__ = . ; 60 | LONG (0x4e560000) /* linkw %fp,#0 */ 61 | *(.fini) 62 | SHORT (0x4e5e) /* unlk %fp */ 63 | SHORT (0x4e75) /* rts */ 64 | 65 | _etext = .; 66 | *(.lit) 67 | } > ram2 68 | 69 | .data : 70 | { 71 | *(.shdata) 72 | *(.data) 73 | _edata = .; 74 | } > ram2 75 | 76 | .bss : 77 | { 78 | . = ALIGN(0x4); 79 | __bss_start = . ; 80 | *(.shbss) 81 | *(.bss) 82 | *(COMMON) 83 | __bss_end = .; 84 | _end = ALIGN (0x8); 85 | __end = _end; 86 | } > ram2 87 | 88 | .stab 0 (NOLOAD) : 89 | { 90 | *(.stab) 91 | } 92 | 93 | .stabstr 0 (NOLOAD) : 94 | { 95 | *(.stabstr) 96 | } 97 | } -------------------------------------------------------------------------------- /PROJ/makefile: -------------------------------------------------------------------------------- 1 | # 2 | # IMPORTANT CRITICAL ACTUAL REAL GENUINE NOTICE!!! 3 | # You can't use std libs or default libds. They unfortunately cause a crash that I don't understand. 4 | # I suspect it has to do with the linker script. 5 | # 6 | LDFILE = linker 7 | srcs: main.c 8 | m68k-elf-gcc -o srcs main.c -O2 -fno-use-linker-plugin -std=gnu99 -Wall -Wno-char-subscripts -nodefaultlibs -nostdlib -mc68000 -mno-align-int -mno-strict-align -T linker 9 | objcopy -O binary srcs sdrv.bin -------------------------------------------------------------------------------- /PROJ/sdrv.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/PROJ/sdrv.bin -------------------------------------------------------------------------------- /PROJ/srcs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/PROJ/srcs -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 68k 2 | SEGA Saturn SCSP Command & Control Driver and Linked Library 3 | by Ponut64 4 | Hitherto otherwise known as "ponèSound" 5 | 6 | Currently supports playback of uncompressed 8-bit signed and 16-bit big-endian signed raw audio samples of up to 65536 samples in size. That would make 64KB the largest 8-bit raw size, and 128kb the largest 16-bit raw size. 7 | 8 | Additionally supports real-time decompression and playback of ADX sound data by sound CPU from sound RAM. Three channels are supported, provided the bitrate of each channel is low enough. The available bitrates for use with ADX are 23040 Hz, 15360 Hz, 11520 Hz, and 7680 Hz. Only one bitrate can be properly decompressed by the Sound CPU at a time due to the need to build multiplication tables for ADX' advanced decompression method. That doesn't mean bitrates can't be mixed, it is just that quality is not guaranteed. 9 | The channel allowance is as follows: 10 | 1 23040 Hz, 11 | 1 15360 Hz + 1 "Ultra Low" Hz, 12 | 2 11520 Hz, 13 | 1 11520Hz + 1 "Ultra Low" Hz, 14 | 3 "Ultra Low" Hz 15 | "Ultra Low Hz" being interchangable with 7680 Hz, 5760 Hz, and 3840 Hz 16 | 17 | The driver has two linked libraries. 18 | Primarily there is pcmsys.c and pcmsys.h ; these are the files needed for general sound playback. 19 | Additionally, there is pcmstm.c and pcmstm.h ; these are the files needed for streaming audio. 20 | With pcmstm.c, one channel of PCM can be streamed at up to 30720, mono, 8-bit with an ADX stream. 21 | The ADX stream can be up to 23040, mono. 22 | It's important to note that due to the low buffer size, the ADX stream starts playback quickly. 23 | It therefore carries some relevance for use in general sound design (like, for voice lines). 24 | However, the raw PCM stream takes significant time to buffer. 25 | 26 | It should be noted that CD-DA makes much better use of CD bandwidth for music. 27 | The downside is that CD-DA tracks are big, and inflexible in size. 28 | CD-DA also locks access to the CD drive when active; it takes 100% of the bandwidth. 29 | 30 | There are two seprate usage demos: 31 | jo_demo, which is for the general-basic functions in pcmsys.c/h. 32 | jo_stream_demo, which is a demonstration of the streaming libraries. 33 | 34 | Linker script from CyberWarriorX 35 | 36 | Compiler was dug up from the internet. 37 | -------------------------------------------------------------------------------- /documentation.md: -------------------------------------------------------------------------------- 1 | // 2 | // Documentation 3 | // 4 | // 5 | 6 | What follows is a basic documentation of what/how Ponésound is. 7 | 8 | //////////////////////////////////////////////////////////////////////// 9 | // *********************PLEASE READ THIS PART*************************** 10 | // IMPORTANT NOTES ABOUT PCM CHANNEL CONTROL 11 | //////////////////////////////////////////////////////////////////////// 12 | 13 | Within the driver, there are four basic control types for PCM sound effects: 14 | Volatile, Protected, Semi-protected, and looping. 15 | Let us go through these types so that I may describe what their names mean and how they should be used. 16 | 17 | 1. Volatile, defined as: 18 | #define PCM_VOLATILE (0) 19 | This should !!!NEVER!!! be used unless you KNOW WHAT YOU ARE DOING. 20 | Volatile sounds have **NO CHANNEL MANAGEMENT!**. For this reason, ADX sounds cannot be played as volatile. 21 | A volatile sound will find the first unprotected channel and select it for playback of the sound. 22 | It will not protect the channel nor will the driver remember that channel as being used for anything. 23 | This play type remains in the driver in case the user wishes to do manual channel management. 24 | I remind you: THIS SHOULD NEVER BE USED unless you want the driver to do nothing with a sound except play it. 25 | 26 | 2. Protected, defined as: 27 | #define PCM_PROTECTED (-1) 28 | With this type, the first free channel will be found and playback for this sound will start. 29 | This is a channel control type which will protect the channel the sound is playing back in. 30 | It will also protect the channel from play commands from the SH2. 31 | That means a sound played back as `PCM_PROTECTED` will not stop or restart unless finished or commanded to stop. 32 | 33 | 3. Semi-protected, defined as: 34 | #define PCM_SEMI (-2) 35 | With this type, the first free channel will be found and playback for this sound will start. 36 | This channel control type also protects the channel that the sound is playing back in. 37 | However, it will not protect the slot from play commands from SH2, such that another play command will immediately restart it. 38 | That means a sound played back as `PCM_SEMI` will start or restart when commanded to play, and stop when commanded to. 39 | 40 | 4. Looping, defined with various types: 41 | #define PCM_ALT_LOOP (3) 42 | 43 | #define PCM_RVS_LOOP (2) 44 | 45 | #define PCM_FWD_LOOP (1) 46 | 47 | All of these types are technically variants of `PCM_PROTECTED`, except they command the SCSP to perform one of three types of loop. 48 | With `PCM_RVS_LOOP`, the sound will loop backwards. With `PCM_FWD_LOOP`, the sound will loop forwards. 49 | With `PCM_ALT_LOOP`, the sound will alternate between looping backwards and forwards. 50 | These are just hardware features that I decided to support; I've no idea why you'd want anything but `PCM_FWD_LOOP`. 51 | Only `PCM_FWD_LOOP` applies to ADX sounds (and it's a sketchy implementation at that). 52 | 53 | 5. ADX Streams, defined as: 54 | #define ADX_STREAM (-3) 55 | Ignore this one. The linked library `pcmstm.c/h` uses it to communicate to the driver that an ADX sound will use a ring buffer. 56 | It does not have any other purpose. 57 | 58 | //////////////////////////////////////////////////////////////////////// 59 | // Notes about the ADX implementation 60 | 61 | The ADX playback is *very strictly* limited to the following rates: 62 | 7680 Hz (NTSC) | 6400 Hz (PAL) for up to 3 channels 63 | 11520 Hz (NTSC) | 9600 Hz (PAL) for up to 2 channels 64 | 15360 Hz (NTSC) | 12800 Hz (PAL) for 1 channel 65 | 23040 Hz (NTSC) | 19200 Hz (PAL) *(only in cases of low sound system activity) 66 | 67 | You can find more detail about bitrates in comments inside the driver source code itself. 68 | The ADX decompression is implemented on the driver-side, aka, the M68k is performing the decompression. 69 | 70 | It is doing so poorly; with shortcuts and tables abound. These compromise quality. 71 | I'll just put in that brief statement so you know that ADX doesn't normally sound this bad. 72 | 73 | Another note is that the driver requires an ADX Master frequency to start. 74 | You supply this when you load the driver. 75 | They are defined as: 76 | #define ADX_MASTER_768 (0) 77 | #define ADX_MASTER_1152 (1) 78 | #define ADX_MASTER_1536 (2) 79 | #define ADX_MASTER_2304 (3) 80 | For PAL, it is defined as: 81 | #define ADX_PAL_640 (4) 82 | #define ADX_PAL_960 (5) 83 | #define ADX_PAL_1280 (6) 84 | #define ADX_PAL_1920 (7) 85 | 86 | The reason a master frequency is required is because the driver builds decompression tables based on this frequency. 87 | You can play back sounds that don't match the master frequency, but they will not sound right. 88 | 89 | /////////////////////////////////////////////////////////////// 90 | // Wait a minute, why are the PAL rates lower, or different at all? 91 | 92 | This is a software-timed driver that is timed on vblank. 93 | All of the channel timers are timed based on the bytes that each bitrate consumes per vblank. 94 | For NTSC, this is 1/60th of a second, or about 16.6ms. For PAL, this is 1/50th of a second, or about 20ms. 95 | You can see then that NTSC screens refresh *faster* and therefore more data is consumed per vblank. 96 | That means the bitrate can be higher. 97 | 98 | The reality is that the PAL system does have more time between vblanks to decompress the data for the next vblank, 99 | so hypothetically, the PAL driver should be able to operate more channels simultaneously. 100 | Unfortunately, I just don't care enough to tell you if it really can or not. 101 | None of the streaming systems are guaranteed to work in PAL either. 102 | 103 | /////////////////////////////////////////////////////////////// 104 | // Wait a minute, I commanded a sound to play twice but it's still only on one channel. What gives?! 105 | 106 | The driver does not instance sound effects for you; you must do that on your own. 107 | It only manages the channels for the sounds as listed in `m68k_com->pcmCtrl`. 108 | In order to instance a sound effect, you must copy the contents of the `pcmCtrl` entry (what is returned from loading the sound) 109 | into `m68k_com->pcmCtrl[numberPCMs]` entry, then perform `numberPCMs++`. 110 | The driver should be able to handle out-of-order instancing (e.g. setting aside certain entries for instancing). 111 | 112 | // But why don't you do that for me? It seems like an obvious feature... 113 | 114 | The 68k's subsystem has the discrete potential to be heavily bottlenecked on memory bandwidth. 115 | The way the driver is currently designed to work, sound instancing would make the 68k responsible for more data management. 116 | Because of the performance concern, sound instancing should not be performed "on the fly"; 117 | instances should be managed after they are loaded, but before they are played, for ideal performance. 118 | This is especially true in the case where ADX and/or sound streaming is being used. 119 | 120 | How much more frustrated would you be with me if loading a sound also required an instance number argument? 121 | And playing a sound required not only a sound number, but an instance number? 122 | 123 | Also, I didn't need it, so I didn't add it. :) 124 | 125 | /////////////////////////////////////////////////////////////// 126 | // 127 | // DRIVER WORK-FLOW 128 | // 129 | /////////////////////////////////////////////////////////////// 130 | 131 | The core of ponésound is the code that runs on the 68K inside of the Sega Saturn. 132 | This code will hitherto be referred to as "the binary", or "the driver". 133 | 134 | The driver is designed to operate on a vblank cycle. If you did not know, "vblank" is short for "Vertical Blank In Interrupt". It will also work on blank OUT. This is unlike most sound control software, which tends to run on-demand or by hardware timed registers. The driver also does not control this timing; it does not have any guarantee, limit, stop or overrun management despite it being designed to operate specifically on a vblank timing. The driver relies on the SH2, and thus the linked library, in order to function on this timing. So while the core is the binary, the linked library that your project must incorporate is also 100% essential to the function of the driver. The simple result of timing the driver's operation based on vblank is that, assuming the driver never runs for more than vblank interval (the time from one vblank to the next), the driver will always be able to start operation assuming a consistent amount of time has passed. For NTSC, this is 1/60th of a second, and for PAL, it is 1/50th of a second. 135 | 136 | The reason that the linked library is needed for the driver to operate based on vblank is because the 68K cannot access the CD system to load its own binary, and it cannot access the relevant SCU registers in order to write its own interrupt state. In fact, the linked library itself does not register any interrupts for the 68K or SCSP, either between each other or the SCU. When the driver has gone through its work-flow, it will end in a loop, waiting for the CPUs to write a 1 (literal 1) where the driver will write a 0 (literal 0) after starting its work. If that is done at vblank, the correct work-flow interval for the driver will be achieved. 137 | 138 | This is done by the following line of code: 139 | 140 | ```m68k_com->start = 1;``` on the CPU side, and immediately thereafter, the driver will start its work-flow. 141 | It will also immediately write back: 142 | ```sh2Com->start = 0;``` 143 | 144 | The area where this data is written to is extremely important to coordinate. After all, the binary is NOT cross-compiled with your project's code. This means the driver does NOT synchronize objects or addresses with your project at compile time. The driver binary is compiled separately. As such, the location of communication data between the driver and the CPUs is at a fixed, mutually agreed upon location in sound RAM. This data is housed in a struct. This is also a mutually agreed upon struct; if you change the contents/layout of this struct in the linked library (pcmsys.h), you must also change it in the driver source code, recompile it, then copy over the new binary to your CD directory. This work-flow is a consequence of not being cross-compiled. The benefit of not being cross-compiled is that you don't have to set up a another cross-compiler and linker script, and you also don't have to suffer the consequences upon the compiler and optimization flags between two different architectures. 145 | 146 | The mutually-agreed upon location of the communication struct is established by the following lines, mirrored in the linked library (pcmsys.h) and the driver source code: 147 | ```#define DRV_SYS_END (47 * 1024) // System defined safe end of driver's address space``` 148 | On the driver's side (marked as SH2 Com because the driver uses this to communicate with the SH2): 149 | ```volatile sysComPara *sh2Com = (volatile sysComPara *) (ADDR_PRG + DRV_SYS_END);``` 150 | On the SH2's side, marked as M68K Com, because ditto: 151 | ```sysComPara * m68k_com = (sysComPara *)((SNDPRG + DRV_SYS_END) | 0x20000000);``` 152 | Note the type difference. Doesn't mean anything to me, it just ended up working that way. 153 | 154 | The `ADDR_PRG` and `SNDPRG` labels are ambiguous mirrors; I should fix that. 155 | These refer to the same concept: On the Saturn's M68K, the first 1KB of addressable memory in sound RAM is reserved as an interrupt vector table. Why it has 1kb of that is strange to me, but I suppose each instrument in sequenced music could reserve an interrupt? No idea, it's probably just a standard part of an M68K's operation, or something. 156 | Anyway, these labels both equate to the start of sound RAM plus `0x408`, or 1032 bytes. Why that, and not 1024 ? Well, 1032 didn't crash. Soo... 157 | 158 | The communication struct at those addresses is as follows: 159 | ``` 160 | typedef struct { 161 | volatile unsigned int adx_stream_length; // Length of the ADX stream (in ADX frames) 162 | volatile unsigned short start; // System Start Boolean 163 | volatile char adx_buffer_pass[2]; // Booleans 164 | volatile short drv_adx_coef_1; // The (signed!) coefficient 1 the driver will 165 | // use to build ADX multiplication tables. 166 | volatile short drv_adx_coef_2; // The (signed!) coefficient 2 the driver will 167 | // use to build ADX multiplication tables. 168 | volatile _PCM_CTRL *pcmCtrl; 169 | volatile unsigned char cdda_left_channel_vol_pan; // Redbook left channel volume & pan. 170 | volatile unsigned char cdda_right_channel_vol_pan; // Redbook right channel volume & pan. 171 | } sysComPara; 172 | ``` 173 | The most important member of this communication struct, aside from `start`, is the `pcmCtrl` pointer. 174 | This is an SH2-mapped pointer to the list of sounds registered with the driver. 175 | Note the driver does not use this pointer; only the linked library (SH2) does. 176 | The driver addresses this data as an array, not a pointer. 177 | 178 | This struct has two elements which are only communicated on first boot of the driver, that being the ADX coefficients. 179 | The way the ADX coefficients affect the ADX playback will be covered in a different part of the documentation. 180 | 181 | Most of the real-time communication data that the driver and CPU bounce back and forth are data which deals with streaming audio. 182 | With regards to non-streaming audio, the master CPU does not do a whole lot with the sound. It generally just writes to play the sound, and the driver decides what to do based on the configuration of the sound. With streaming audio however, the driver must communicate back to the CPU the state of playback. 183 | 184 | When the driver is started for the first time, the 68K's program counter will be at the previously mentioned address: `0x408` 185 | To jump the program counter to the appropriate point in the code, the binary is front-loaded at this address with a jump instruction to send it to the main program management loop. That loop is at the `_start` label. Note this is not normal C `main`, because the driver is not built with a normal C linker script. I got it from CyberWarriorX and that's what it does. 186 | 187 | ``` 188 | void lead_function(void) // Link start to main 189 | { 190 | // note that _start is the ASM label that equates to 191 | // the lead function in this compiler. In a normal 192 | // compiler, it would be "main". 193 | __asm__("jmp _start"); 194 | } 195 | ``` 196 | 197 | The start function leads with the driver boot functions before proceeding to the main loop which will capture the program counter. This boot function is called `driver_data_init`. 198 | In this function, the driver will clean the sound slot registers, set pointers for the SH2, set up the CDDA channels, initialize the PCM control system, build the ADX multiplication tables and control data. Of special note is that the ADX tables will take a lot of time to finish building. Users should be aware that the driver needs time to do this before it is ready to play sound. 199 | 200 | Past this, the work-flow of the driver will "proceed as normal" through checking the status of each member of the PCM_CTRL struct. The function which does this is aptly named `pcm_control_loop`. After that, the CD-DA volume is managed (so you can change it at any time). 201 | 202 | This concludes the general work-flow of the driver itself. If you want more detail, the source code is public. 203 | You will note this section did not cover much of anything regarding the linked library, which is needed to use the driver. 204 | That is for the following section. 205 | 206 | //////////////////////////////////////////////////////////////////////// 207 | // 208 | // LIBRARY WORK-FLOW 209 | // 210 | //////////////////////////////////////////////////////////////////////// 211 | 212 | 213 | 214 | //// 215 | // Notes 216 | //// 217 | 218 | 219 | 220 | -------------------------------------------------------------------------------- /jo_cdda_demo/cd/ABS.TXT: -------------------------------------------------------------------------------- 1 | NOT Abstracted by SEGA 2 | 3 | -------------------------------------------------------------------------------- /jo_cdda_demo/cd/BIB.TXT: -------------------------------------------------------------------------------- 1 | NOT Bibliographiced by SEGA 2 | 3 | -------------------------------------------------------------------------------- /jo_cdda_demo/cd/CPY.TXT: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /jo_cdda_demo/cd/Nbgm.ADX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_cdda_demo/cd/Nbgm.ADX -------------------------------------------------------------------------------- /jo_cdda_demo/cd/def_flag.PCM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_cdda_demo/cd/def_flag.PCM -------------------------------------------------------------------------------- /jo_cdda_demo/cd/hav_flag.PCM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_cdda_demo/cd/hav_flag.PCM -------------------------------------------------------------------------------- /jo_cdda_demo/cd/sdrv.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_cdda_demo/cd/sdrv.bin -------------------------------------------------------------------------------- /jo_cdda_demo/clean.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET COMPILER_DIR=..\..\Compiler 3 | SET JO_ENGINE_SRC_DIR=../../jo_engine 4 | SET PATH=%COMPILER_DIR%\WINDOWS\Other Utilities;%PATH% 5 | 6 | rm -f ./cd/0.bin 7 | rm -f *.o 8 | rm -f %JO_ENGINE_SRC_DIR%/*.o 9 | rm -f ./*.bin 10 | rm -f ./*.coff 11 | rm -f ./*.elf 12 | rm -f ./*.map 13 | rm -f ./*.iso 14 | rm -f ./*.cue 15 | 16 | ECHO Done. 17 | -------------------------------------------------------------------------------- /jo_cdda_demo/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f ./cd/0.bin 3 | rm -f *.o 4 | rm -f ../../jo_engine/*.o 5 | rm -f ./*.bin 6 | rm -f ./*.coff 7 | rm -f ./*.elf 8 | rm -f ./*.map 9 | rm -f ./*.iso 10 | rm -f ./*.cue 11 | echo "Done." 12 | exit 0 13 | -------------------------------------------------------------------------------- /jo_cdda_demo/compile.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET COMPILER_DIR=..\..\Compiler 3 | SET PATH=%COMPILER_DIR%\WINDOWS\Other Utilities;%COMPILER_DIR%\WINDOWS\bin;%PATH% 4 | make re 5 | PAUSE -------------------------------------------------------------------------------- /jo_cdda_demo/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export NCPU=`nproc` 3 | make clean && make -j${NCPU} all 4 | exit 0 5 | 6 | -------------------------------------------------------------------------------- /jo_cdda_demo/end_times.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_cdda_demo/end_times.wav -------------------------------------------------------------------------------- /jo_cdda_demo/main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "pcmsys.h" 4 | 5 | int framerate; 6 | 7 | //Sound Numbers 8 | short pcm8snd; 9 | short pcm16snd; 10 | short adx4snd; 11 | // 12 | 13 | void my_draw(void) 14 | { 15 | jo_printf(0, 5, "P64 PCM + CD-DA Usage Demo"); 16 | jo_printf(0, 7, "Simple warnings:"); 17 | jo_printf(0, 8, "1. Be VERY CAREFUL with the track setting."); 18 | jo_printf(0, 9, "Compile first, then check the '.cue' file."); 19 | jo_printf(0, 10, "Select tracks accordingly, and recompile."); 20 | 21 | jo_printf(0, 12, "2. Redbook audio tracks MUST be stereo."); 22 | jo_printf(0, 13, "Should be be 16-bit wav files."); 23 | jo_printf(0, 14, "Some CD build tools will auto-convert."); 24 | jo_printf(0, 15, "No matter what format they are originally,"); 25 | jo_printf(0, 16, "they'll be specially encoded as audio tracks"); 26 | jo_printf(0, 17, "Redbook tracks are 44.1 KHz, Stereo, 16-bit."); 27 | 28 | jo_printf(0, 19, "3. Redbook tracks are huge."); 29 | jo_printf(0, 20, "Even for a small project, be careful."); 30 | jo_printf(0, 21, "You could run out of CD space using CDDA."); 31 | 32 | jo_printf(0, 24, "Press Z to start CD-DA"); 33 | jo_printf(0, 25, "Press Y to make CD-DA quiet"); 34 | 35 | // jo_printf(0, 18, "Y pans sound from A button"); 36 | 37 | if(jo_is_input_key_down(0, JO_KEY_A)) 38 | { 39 | pcm_play(adx4snd, PCM_SEMI, 7); 40 | } 41 | 42 | if(jo_is_input_key_pressed(0, JO_KEY_B)) 43 | { 44 | //jo_printf(0, 14, "(--Loop Play--)"); 45 | pcm_play(pcm16snd, PCM_ALT_LOOP, 6); 46 | } else { 47 | //jo_printf(0, 14, "(--Loop Stop--)"); 48 | pcm_cease(pcm16snd); 49 | } 50 | 51 | if(jo_is_input_key_pressed(0, JO_KEY_C)) 52 | { 53 | pcm_play(pcm8snd, PCM_PROTECTED, 6); 54 | } 55 | 56 | if(jo_is_input_key_pressed(0, JO_KEY_Z)) 57 | { 58 | CDDA_PlaySingle(2, true); 59 | } 60 | 61 | if(jo_is_input_key_pressed(0, JO_KEY_Y)) 62 | { 63 | CDDA_SetVolume(2); 64 | } 65 | 66 | if(jo_is_input_key_pressed(0, JO_KEY_X)) 67 | { 68 | pcm_cease(adx4snd); 69 | } 70 | 71 | //slSynch(); 72 | } 73 | 74 | void jo_main(void) 75 | { 76 | jo_core_init(JO_COLOR_Black); 77 | 78 | load_drv(ADX_MASTER_2304); 79 | //////////////////////////////////////////////// 80 | // REMINDER: All file names must comply with the 8.3 standard. 81 | // File extensions can be no longer than 3 letters. 82 | // File names can be no longer than 8 letters. 83 | // The total length is thusly 12 characters (as there is a period). 84 | //////////////////////////////////////////////// 85 | /* 86 | To convert a sound to 16-bit 87 | ffmpeg -i %this%.wav -f s16be -ac 1 -ar (bitrate) %this%.PCM 88 | To convert a sound to 8-bit 89 | ffmpeg -i %this%.wav -f s8 -ac 1 -ar (bitrate) %this%.PCM 90 | */ 91 | pcm16snd = load_16bit_pcm((Sint8 *)"DEF_FLAG.PCM", 15360); 92 | pcm8snd = load_8bit_pcm((Sint8 *)"HAV_FLAG.PCM", 15360); 93 | adx4snd = load_adx((Sint8 *)"NBGM.ADX"); 94 | 95 | slZoomNbg0(65535, 73720); 96 | jo_core_add_vblank_callback(sdrv_vblank_rq); 97 | jo_core_add_callback(my_draw); 98 | jo_core_run(); 99 | } 100 | 101 | /* 102 | ** END OF FILE 103 | */ 104 | 105 | -------------------------------------------------------------------------------- /jo_cdda_demo/makefile: -------------------------------------------------------------------------------- 1 | JO_COMPILE_WITH_VIDEO_MODULE = 1 2 | JO_COMPILE_WITH_BACKUP_MODULE = 0 3 | JO_COMPILE_WITH_TGA_MODULE = 0 4 | JO_COMPILE_WITH_AUDIO_MODULE = 0 5 | JO_COMPILE_WITH_3D_MODULE = 0 6 | JO_COMPILE_WITH_PSEUDO_MODE7_MODULE = 0 7 | JO_COMPILE_WITH_EFFECTS_MODULE = 0 8 | JO_COMPILE_WITH_FS_MODULE = 0 9 | JO_PSEUDO_SATURN_KAI_SUPPORT = 1 10 | JO_COMPILE_WITH_DUAL_CPU_MODULE = 0 11 | JO_DEBUG = 0 12 | JO_COMPILE_USING_SGL=1 13 | JO_NTSC = 1 14 | SRCS=main.c pcmsys.c 15 | JO_ENGINE_SRC_DIR=../../jo_engine 16 | COMPILER_DIR=../../Compiler 17 | include $(COMPILER_DIR)/COMMON/jo_engine_makefile 18 | -------------------------------------------------------------------------------- /jo_cdda_demo/pcmsys.c: -------------------------------------------------------------------------------- 1 | //pcm_sys.c 2 | //this file is compiled separately 3 | //hopefully somewhat portable 4 | // 5 | #include //Mostly to link us with SBL file system 6 | #include "pcmsys.h" 7 | #include 8 | #define true (1) 9 | #define false (0) 10 | 11 | static const int logtbl[] = { 12 | /* 0 */ 0, 13 | /* 1 */ 1, 14 | /* 2 */ 2, 2, 15 | /* 4 */ 3, 3, 3, 3, 16 | /* 8 */ 4, 4, 4, 4, 4, 4, 4, 4, 17 | /* 16 */ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 18 | /* 32 */ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 19 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 20 | /* 64 */ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 24 | /* 128 */ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 25 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 26 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 28 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 29 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 30 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 31 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 32 | }; 33 | 34 | #define PCM_MSK1(a) ((a)&0x0001) 35 | #define PCM_MSK3(a) ((a)&0x0007) 36 | #define PCM_MSK4(a) ((a)&0x000F) 37 | #define PCM_MSK5(a) ((a)&0x001F) 38 | #define PCM_MSK10(a) ((a)&0x03FF) 39 | 40 | #define PCM_SCSP_FREQUENCY (44100L) 41 | 42 | #define PCM_CALC_OCT(smpling_rate) \ 43 | ((int)logtbl[PCM_SCSP_FREQUENCY / ((smpling_rate) + 1)]) 44 | 45 | #define PCM_CALC_SHIFT_FREQ(oct) \ 46 | (PCM_SCSP_FREQUENCY >> (oct)) 47 | 48 | #define PCM_CALC_FNS(smpling_rate, shift_freq) \ 49 | ((((smpling_rate) - (shift_freq)) << 10) / (shift_freq)) 50 | 51 | #define PCM_SET_PITCH_WORD(oct, fns) \ 52 | ((int)((PCM_MSK4(-(oct)) << 11) | PCM_MSK10(fns))) 53 | 54 | sysComPara * m68k_com = (sysComPara *)((SNDPRG + DRV_SYS_END) | 0x20000000); 55 | static unsigned int * scsp_loading_start = (unsigned int*)(0x408 + DRV_SYS_END + 0x20); //Local loading address for sound data, is DRV_SYS_END ahead of the SNDPRG, and ahead of the communication data 56 | unsigned int * scsp_load; 57 | unsigned short * master_volume = (unsigned short *)(SNDRAM + 0x100400); 58 | unsigned short driver_master_volume = 0; 59 | short numberPCMs = 0; 60 | 61 | static short adx_coef_tbl[8][2] = 62 | { 63 | {ADX_768_COEF_1, ADX_768_COEF_2}, 64 | {ADX_1152_COEF_1, ADX_1152_COEF_2}, 65 | {ADX_1536_COEF_1, ADX_1536_COEF_2}, 66 | {ADX_2304_COEF_1, ADX_2304_COEF_2}, 67 | {ADX_640_COEF_1, ADX_640_COEF_2}, 68 | {ADX_960_COEF_1, ADX_960_COEF_2}, 69 | {ADX_1280_COEF_1, ADX_1280_COEF_2}, 70 | {ADX_1920_COEF_1, ADX_1920_COEF_2} 71 | }; 72 | 73 | //MVOL is a 4-bit number; values 0-15 are valid. 74 | //If the value is higher than 15, this will just cap it at 15. 75 | void set_master_volume(unsigned short volume) 76 | { 77 | volume = (volume >= 0xF) ? 0xF : volume; 78 | *master_volume = 0x200 | (volume & 0xF); 79 | driver_master_volume = volume; 80 | } 81 | 82 | void pcm_play(short pcmNumber, char ctrlType, char volume) 83 | { 84 | if(pcmNumber < 0) return; 85 | m68k_com->pcmCtrl[pcmNumber].sh2_permit = 1; 86 | m68k_com->pcmCtrl[pcmNumber].volume = volume; 87 | m68k_com->pcmCtrl[pcmNumber].loopType = ctrlType; 88 | } 89 | 90 | void pcm_parameter_change(short pcmNumber, char volume, char pan) 91 | { 92 | if(pcmNumber < 0) return; 93 | m68k_com->pcmCtrl[pcmNumber].volume = volume; 94 | m68k_com->pcmCtrl[pcmNumber].pan = pan; 95 | } 96 | 97 | void pcm_cease(short pcmNumber) 98 | { 99 | if(pcmNumber < 0) return; 100 | if(m68k_com->pcmCtrl[pcmNumber].loopType <= 0) //If it is a volatile or protected sound, the expected control method is to mute the sound and let it end itself. 101 | { //Protected sounds have a permission state of "until they end". 102 | m68k_com->pcmCtrl[pcmNumber].volume = 0; 103 | } else { 104 | m68k_com->pcmCtrl[pcmNumber].sh2_permit = 0; //If it is a looping sound, the control method is to command it to stop. 105 | } 106 | } 107 | 108 | // 109 | // Usage: 110 | // Intended as the "level reset" function. 111 | // Does not soft or hard reset driver. To do that, re-load the driver binary (run load_drv again). 112 | // This instead resets the loading pointer and number of PCMs to a specific PCM number. 113 | // In use with proper sequence of asset loading, a certain number of sound assets can be retained in sound memory, with others discarded. 114 | // 115 | // The argument "highest_pcm_number_to_keep" is the latest sequentially loaded PCM in sound RAM that signals the point at which: 116 | // Any PCM number loaded earlier than this will be kept in memory and its number still valid to play the sound. 117 | // Any PCM number loaded later than this will be ignored in memory when loading new sounds, but the number is still valid to play sound. 118 | // Pass -1 to this function to clear all PCMs. 119 | void pcm_reset(short highest_pcm_number_to_keep) 120 | { 121 | //For clearing all sounds, input is negative 122 | if(highest_pcm_number_to_keep < 0) { 123 | scsp_load = scsp_loading_start; 124 | numberPCMs = 0; 125 | return; 126 | } 127 | 128 | numberPCMs = highest_pcm_number_to_keep+1; 129 | scsp_load = (unsigned int *)((unsigned int)(m68k_com->pcmCtrl[highest_pcm_number_to_keep].hiAddrBits<<16) | (int)(m68k_com->pcmCtrl[highest_pcm_number_to_keep].loAddrBits)); 130 | if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 2) 131 | { //If this is an ADX sound, offset the loading pointer by # of frames by 18. Address includes 18-byte header offset. 132 | scsp_load = (unsigned int *)((unsigned int)scsp_load + (m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize * 18)); 133 | } else if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 1) 134 | { //If this is an 8-bit PCM, offset the loading pointer by the playsize, exactly (one byte samples). 135 | scsp_load = (unsigned int *)((unsigned int)scsp_load + m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize); 136 | } else if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 0) 137 | { //If this is a 16-bit PCM, offset the loading pointer by the playsize, shifted left once (two byte samples). 138 | scsp_load = (unsigned int *)((unsigned int)scsp_load + (m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize<<1)); 139 | } 140 | } 141 | 142 | /**stolen from xl2**/ 143 | #define OPEN_MAX (Sint32)5 144 | #define DIR_MAX (Sint32)1024 145 | #define RD_UNIT (10) 146 | #define SECT_SIZE (2048) 147 | GfsDirTbl gfsDirTbl; 148 | GfsDirName gfsDirName[DIR_MAX]; 149 | Uint32 gfsLibWork[GFS_WORK_SIZE(OPEN_MAX)/sizeof(Uint32)]; 150 | Sint32 gfsDirN; 151 | void cd_init(void) 152 | { 153 | GFS_DIRTBL_TYPE(&gfsDirTbl) = GFS_DIR_NAME; 154 | GFS_DIRTBL_DIRNAME(&gfsDirTbl) = gfsDirName; 155 | GFS_DIRTBL_NDIR(&gfsDirTbl) = DIR_MAX; 156 | gfsDirN = GFS_Init(OPEN_MAX, gfsLibWork, &gfsDirTbl); 157 | } 158 | 159 | void smpc_wait_till_ready (void) 160 | { 161 | // Wait until SF register is cleared 162 | while(SMPC_REG_SF & 0x1) { } 163 | } 164 | 165 | ////////////////////////////////////////////////////////////////////////////// 166 | 167 | void smpc_issue_command(unsigned char cmd) 168 | { 169 | // Set SF register so that no other command can be issued. 170 | SMPC_REG_SF = 1; 171 | 172 | // Writing COMREG starts execution 173 | SMPC_REG_COMREG = cmd; 174 | } 175 | 176 | ////////////////////////////////////////////////////////////////////////////// 177 | 178 | void load_driver_binary(Sint8 * filename, void * buffer, int master_adx_frequency) 179 | { 180 | cd_init(); 181 | GfsHn s_gfs; 182 | Sint32 file_size; 183 | 184 | Sint32 local_name = GFS_NameToId(filename); 185 | 186 | //Open GFS 187 | s_gfs = GFS_Open((Sint32)local_name); 188 | //Get file information (mostly, the file size) 189 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 190 | 191 | GFS_Close(s_gfs); 192 | 193 | GFS_Load(local_name, 0, (Uint32 *)buffer, file_size); 194 | 195 | //The immediacy of these commands is important. 196 | //As per SEGA technical bulletin 51, the Sound CPU is not to be turned off for more than 0.5 seconds. 197 | // Turn off Sound CPU 198 | smpc_issue_command(SMPC_CMD_SNDOFF); 199 | smpc_wait_till_ready(); 200 | //Set max master volume + 4mbit memory 201 | set_master_volume(0xF); 202 | //Copy the driver binary (code) over to sound RAM. The binary includes the vector table information. 203 | slDMACopy(buffer, (void*)SNDRAM, file_size); 204 | slDMAWait(); 205 | //Set the ADX coefficients for the driver to use, if one was selected. 206 | 207 | m68k_com->drv_adx_coef_1 = adx_coef_tbl[master_adx_frequency][0]; 208 | m68k_com->drv_adx_coef_2 = adx_coef_tbl[master_adx_frequency][1]; 209 | 210 | // Turn on Sound CPU again 211 | smpc_wait_till_ready(); 212 | smpc_issue_command(SMPC_CMD_SNDON); 213 | // 214 | } 215 | 216 | void load_drv(int master_adx_frequency) 217 | { 218 | // Make sure SCSP is set to 512k mode 219 | *(unsigned char *)(0x25B00400) = 0x02; 220 | 221 | // Clear Sound Ram 222 | for (int i = 0; i < 0x80000; i+=4){ 223 | *(unsigned int *)(SNDRAM + i) = 0x00000000; 224 | } 225 | // Volatile loading buffer 226 | // This memory is not permanently used, its just a place to temporarily store the driver binary 227 | void * binary_buffer = (void*)2097152; 228 | 229 | // Copy driver over 230 | load_driver_binary((Sint8*)"SDRV.BIN", binary_buffer, master_adx_frequency); 231 | m68k_com->start = 0xFFFF; 232 | volatile int i = 0; 233 | scsp_load = scsp_loading_start; // Re-set loading pointer. 234 | for(i = 0; i < (int)scsp_load; i++) 235 | { 236 | //This is to pop the stack here. Because GCC. 237 | } 238 | //Additionally, reset the number of PCMs. 239 | numberPCMs = 0; 240 | } 241 | 242 | short calculate_bytes_per_blank(int sampleRate, Bool is8Bit, Bool isPAL) 243 | { 244 | int frameCount = (isPAL == true) ? 50 : 60; 245 | int sampleSize = (is8Bit == true) ? 8 : 16; 246 | return ((sampleRate * sampleSize)>>3)/frameCount; 247 | 248 | } 249 | 250 | short convert_bitrate_to_pitchword(short sampleRate) 251 | { 252 | 253 | int octr; 254 | int shiftr; 255 | int fnsr; 256 | 257 | octr = PCM_CALC_OCT(sampleRate); 258 | shiftr = PCM_CALC_SHIFT_FREQ(octr); 259 | fnsr = PCM_CALC_FNS(sampleRate, shiftr); 260 | 261 | return PCM_SET_PITCH_WORD(octr, fnsr); 262 | } 263 | 264 | short load_16bit_pcm(Sint8 * filename, int sampleRate) 265 | { 266 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 267 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 268 | 269 | GfsHn s_gfs; 270 | Sint32 file_size; 271 | 272 | Sint32 local_name = GFS_NameToId(filename); 273 | 274 | //Open GFS 275 | s_gfs = GFS_Open((Sint32)local_name); 276 | //Get file information (mostly, the file size) 277 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 278 | 279 | GFS_Close(s_gfs); 280 | 281 | if(file_size > (128 * 1024)) return -1; //PCM size too large for general-purpose playback [could still work with timed execution & offets] 282 | 283 | file_size += ((unsigned int)file_size & 1) ? 1 : 0; 284 | file_size += ((unsigned int)file_size & 3) ? 2 : 0; 285 | 286 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), file_size); 287 | 288 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)scsp_load >> 16); 289 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)scsp_load & 0xFFFF); 290 | 291 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(sampleRate); 292 | m68k_com->pcmCtrl[numberPCMs].playsize = (file_size>>1); 293 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = calculate_bytes_per_blank(sampleRate, false, PCM_SYS_REGION); //Iniitalize as max volume 294 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_16BIT; //Select 16-bit 295 | m68k_com->pcmCtrl[numberPCMs].loopType = 0; //Initialize as non-looping 296 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Initialize as max volume 297 | 298 | 299 | numberPCMs++; //Increment pcm # 300 | scsp_load = (unsigned int *)((unsigned int )scsp_load + file_size); 301 | return (numberPCMs-1); //Return the PCM # this sound recieved 302 | } 303 | 304 | short load_8bit_pcm(Sint8 * filename, int sampleRate) 305 | { 306 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 307 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 308 | 309 | GfsHn s_gfs; 310 | Sint32 file_size; 311 | 312 | Sint32 local_name = GFS_NameToId(filename); 313 | 314 | //Open GFS 315 | s_gfs = GFS_Open((Sint32)local_name); 316 | //Get file information (mostly, the file size) 317 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 318 | 319 | GFS_Close(s_gfs); 320 | 321 | 322 | if(file_size > (64 * 1024)) return -1; //PCM size too large for general-purpose playback [could still work with timed execution & offets] 323 | 324 | 325 | file_size += ((unsigned int)file_size & 1) ? 1 : 0; 326 | file_size += ((unsigned int)file_size & 3) ? 2 : 0; 327 | 328 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), file_size); 329 | 330 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)scsp_load >> 16); 331 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)scsp_load & 0xFFFF); 332 | 333 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(sampleRate); 334 | m68k_com->pcmCtrl[numberPCMs].playsize = (file_size); 335 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = calculate_bytes_per_blank(sampleRate, true, PCM_SYS_REGION); //Iniitalize as max volume 336 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_8BIT; //Select 8-bit 337 | m68k_com->pcmCtrl[numberPCMs].loopType = 0; //Initialize as non-looping 338 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Iniitalize as max volume 339 | 340 | 341 | numberPCMs++; //Increment pcm # 342 | scsp_load = (unsigned int *)((unsigned int )scsp_load + file_size); 343 | return (numberPCMs-1); //Return the PCM # this sound received 344 | } 345 | 346 | // Recursive function to return gcd of a and b 347 | short gcd(short a, short b) 348 | { 349 | if (a == 0) 350 | return b; 351 | return gcd(b % a, a); 352 | } 353 | 354 | // Function to return LCM of two numbers 355 | // Used specifically to find the buffer size for ADX sound effects 356 | short lcm(short a, short b) 357 | { 358 | return (a / gcd(a, b)) * b; 359 | } 360 | 361 | short load_adx(Sint8 * filename) 362 | { 363 | static adx_header adx; 364 | 365 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 366 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 367 | 368 | Sint32 local_name = GFS_NameToId(filename); 369 | 370 | ////////////////////////// 371 | // Step 1: Load the size of the header from the file to the header's location 372 | ////////////////////////// 373 | GFS_Load(local_name, 0, (Uint32 *)&adx, sizeof(adx_header)); 374 | ////////////////////////// 375 | // Step 2: Check the data we just loaded and make sure it's an ADX file. 376 | // If the data does not match what the decompression routine expects, this function will return -1. 377 | ////////////////////////// 378 | // jo_printf(13, 9, "ohalf(%i)", adx.one_half); 379 | // jo_printf(13, 10, "blocksz(%i)", adx.block_size); 380 | // jo_printf(13, 11, "bitdepth(%i)", adx.bit_depth); 381 | // jo_printf(13, 12, "srate(%i)", adx.sample_rate); 382 | if(adx.one_half != 32768 || adx.block_size != 18 || adx.bit_depth != 4) return -1; 383 | ////////////////////////// 384 | // Step 3: Parse the data in the header to the sound driver PCM control struct. 385 | // Special things for ADX is the playsize is actually the # of ADX frames, not the number of samples. 386 | // We expect a block size of 16, so each frame has 16 bytes of sample data which is 32 samples. 387 | ////////////////////////// 388 | // Because we ""dumbly"" load the header to sound RAM, let's give the used pointer the offset. 389 | unsigned int working_address = (unsigned int)(scsp_load) + adx.offset2data + 4; 390 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)working_address >> 16); 391 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)working_address & 0xFFFF); 392 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(adx.sample_rate); 393 | m68k_com->pcmCtrl[numberPCMs].playsize = (adx.sample_ct / 32); 394 | short bpb = calculate_bytes_per_blank((int)adx.sample_rate, false, PCM_SYS_REGION); //Iniitalize as max volume 395 | if(bpb != 768 && bpb != 512 && bpb != 384 && bpb != 256 && bpb != 192 && bpb != 128) 396 | { 397 | slPrint("!(ADX INVALID BYTE-RATE)!", slLocate(0, 1)); 398 | return -2; 399 | } 400 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = bpb; 401 | unsigned short big_dct_sz = (bpb >= 256) ? lcm(bpb, bpb + 64)<<1 : 5376; // Dirty fix for ultra low bitrate 402 | m68k_com->pcmCtrl[numberPCMs].decompression_size = (big_dct_sz > (adx.sample_ct<<1)) ? adx.sample_ct<<1 : big_dct_sz; 403 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_ADX; //Select ADX type 404 | m68k_com->pcmCtrl[numberPCMs].loopType = PCM_SEMI; //Initialize as semi-protected. 405 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Iniitalize as max volume 406 | numberPCMs++; 407 | ///////////////////////// 408 | // Step 4: Load the compressed ADX data to sound RAM. Unfortunately, we must include the 20 byte header. 409 | // We want to load from an offset location from CD and then load (sample_ct / 32) * 18 bytes. 410 | // We divide the sample count by 32 to retrieve the ADX frame count. We multiply by 18, as that is the size of an ADX 'frame'. 411 | ///////////////////////// 412 | unsigned int number_of_bytes_to_load = (adx.sample_ct / 32) * 18; 413 | number_of_bytes_to_load += ((unsigned int)number_of_bytes_to_load & 1) ? 1 : 0; 414 | number_of_bytes_to_load += ((unsigned int)number_of_bytes_to_load & 3) ? 2 : 0; 415 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), number_of_bytes_to_load); 416 | scsp_load = (unsigned int *)((unsigned int )scsp_load + number_of_bytes_to_load); 417 | return (numberPCMs-1); //Return the PCM # this sound recieved 418 | } 419 | 420 | void sdrv_vblank_rq(void) 421 | { 422 | //jo_printf(0, 0, "drv_stat(%i)", m68k_com->start); 423 | m68k_com->start = 1; 424 | } 425 | 426 | //////////////////////////////////////////////////////////////////////////////// 427 | // Redbook Support 428 | // These are mostly CDC commands. 429 | // credit to: ndiddy, ReyeMe, CyberWarriorX [iapetus] 430 | //////////////////////////////////////////////////////////////////////////////// 431 | 432 | 433 | void CDDA_SetVolume(int vol) 434 | { 435 | //Step 1: Remove the volume bits from the value (isolate the pan) 436 | unsigned char newvol = m68k_com->cdda_left_channel_vol_pan & 0x1F; 437 | //Step 2: Apply the volume to the correct bits 438 | newvol |= ((vol & 0x7)<<5); 439 | //Step 3: Apply value back to left channel 440 | m68k_com->cdda_left_channel_vol_pan = newvol; 441 | //Step 4: Repeat for right channel 442 | newvol = m68k_com->cdda_right_channel_vol_pan & 0x1F; 443 | //Step 5: Apply the volume to the correct bits 444 | newvol |= ((vol & 0x7)<<5); 445 | //Step 6: Apply value back to right channel 446 | m68k_com->cdda_right_channel_vol_pan = newvol; 447 | } 448 | 449 | // To see what this does and how to use it, refer to the SCSP manual. 450 | // Warning: Use without reading the manual may break your CD audio. 451 | void CDDA_SetChannelVolPan(unsigned char left_channel, unsigned char right_channel) 452 | { 453 | m68k_com->cdda_left_channel_vol_pan = left_channel; 454 | m68k_com->cdda_right_channel_vol_pan = right_channel; 455 | } 456 | 457 | 458 | // ------------------------------------- 459 | // Functions 460 | // ------------------------------------- 461 | 462 | /** @brief Gets table of contents 463 | * @param toc Table of contents data struct 464 | */ 465 | void CDGetTableOfContents(CDTableOfContents * toc) 466 | { 467 | CDC_TgetToc((Uint32*)toc); 468 | } 469 | 470 | /** @brief Play audio 471 | * @param fromTrack Start track 472 | * @param toTrack End track 473 | * @param loop Loop playback 474 | * @param startAddress Start address of the playback 475 | */ 476 | void CDDA_Play(int fromTrack, int toTrack, Bool loop, int startAddress) 477 | { 478 | CdcPly ply; 479 | 480 | // Get TOC 481 | CDTableOfContents toc; 482 | CDGetTableOfContents(&toc); 483 | 484 | // Start of the playback address 485 | CDC_PLY_STYPE(&ply) = CDC_PTYPE_FAD; // track number 486 | 487 | if (startAddress == 0) 488 | { 489 | 490 | CDC_PLY_SFAD(&ply) = toc.Tracks[fromTrack].fad; 491 | } 492 | else 493 | { 494 | CDC_PLY_SFAD(&ply) = startAddress; 495 | } 496 | 497 | if (toTrack + 1 < MAX_CD_TRACK_COUNT) 498 | { 499 | // End of the playback address 500 | CDC_PLY_ETYPE(&ply) = CDC_PTYPE_FAD; 501 | CDC_PLY_EFAS(&ply) = toc.Tracks[toTrack + 1].fad - toc.Tracks[fromTrack].fad; 502 | } 503 | else 504 | { 505 | // End of the playback is end of the disk 506 | CDC_PLY_ETYPE(&ply) = CDC_PTYPE_DFL; 507 | } 508 | 509 | // Playback mode 510 | CDC_PLY_PMODE(&ply) = CDC_PM_DFL | (loop ? 0xf : 0); // 0xf = infinite repetitions 511 | 512 | // Start playback 513 | CDC_CdPlay(&ply); 514 | } 515 | 516 | /** @brief Stop audio playback 517 | * @return Position of where playback stopped 518 | */ 519 | int CDDA_Stop(void) 520 | { 521 | // Get current address 522 | CdcStat stat; 523 | CDC_GetCurStat(&stat); 524 | 525 | // Restore address 526 | CdcPos poswk; 527 | CDC_POS_PTYPE(&poswk) = CDC_PTYPE_DFL; 528 | CDC_CdSeek(&poswk); 529 | 530 | // Last playback address 531 | return stat.report.fad; 532 | } 533 | 534 | void CDDA_PlaySingle(int track, Bool loop) 535 | { 536 | CDDA_Play(track, track, loop, 0); 537 | } 538 | 539 | 540 | -------------------------------------------------------------------------------- /jo_cdda_demo/pcmsys.h: -------------------------------------------------------------------------------- 1 | // 2 | //pcmsys.h 3 | // 4 | #ifndef __PCMSYS_H__ 5 | # define __PCMSYS_H__ 6 | 7 | /////////////// 8 | // Likely duplicates from other libraries (in this case, taken from iapetus) 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #define SMPC_REG_IREG(i) *((volatile unsigned char *)0x20100001+((i) * 2)) 12 | #define SMPC_REG_COMREG *((volatile unsigned char *)0x2010001F) 13 | #define SMPC_REG_OREG(o) *((volatile unsigned char *)0x20100021+((o) * 2)) 14 | #define SMPC_REG_SR *((volatile unsigned char *)0x20100061) 15 | #define SMPC_REG_SF *((volatile unsigned char *)0x20100063) 16 | #define SMPC_REG_PDR1 *((volatile unsigned char *)0x20100075) 17 | #define SMPC_REG_PDR2 *((volatile unsigned char *)0x20100077) 18 | #define SMPC_REG_DDR1 *((volatile unsigned char *)0x20100079) 19 | #define SMPC_REG_DDR2 *((volatile unsigned char *)0x2010007B) 20 | #define SMPC_REG_IOSEL *((volatile unsigned char *)0x2010007D) 21 | #define SMPC_REG_EXLE *((volatile unsigned char *)0x2010007F) 22 | 23 | ////////////////////////////////////////////////////////////////////////////// 24 | 25 | #define SMPC_CMD_MSHON 0x00 26 | #define SMPC_CMD_SSHON 0x02 27 | #define SMPC_CMD_SSHOFF 0x03 28 | #define SMPC_CMD_SNDON 0x06 29 | #define SMPC_CMD_SNDOFF 0x07 30 | #define SMPC_CMD_CDON 0x08 31 | #define SMPC_CMD_CDOFF 0x09 32 | #define SMPC_CMD_CARTON 0x0A 33 | #define SMPC_CMD_CARTOFF 0x0B 34 | #define SMPC_CMD_SYSRES 0x0D 35 | #define SMPC_CMD_CKCHG352 0x0E 36 | #define SMPC_CMD_CKCHG320 0x0F 37 | #define SMPC_CMD_INTBACK 0x10 38 | #define SMPC_CMD_SETTIM 0x16 39 | #define SMPC_CMD_SETSM 0x17 40 | #define SMPC_CMD_NMIREQ 0x18 41 | #define SMPC_CMD_RESENA 0x19 42 | #define SMPC_CMD_RESDIS 0x1A 43 | 44 | ////////////////////////////////////////////////////////////////////////////// 45 | //Address mapping shorthand 46 | #define MAP_TO_SCSP(sh2map_snd_adr) ((sh2map_snd_adr - SNDRAM)) 47 | ////////////////////////////////////////////////////////////////////////////// 48 | //No Touchy Sound Ram Start! 49 | #define SNDRAM (631242752) 50 | // 1KB here is reserved for interrupts 51 | #define SNDPRG (SNDRAM + 0x408) 52 | //Also the end of sound RAM 53 | #define PCMEND (SNDRAM + 0x7F000) 54 | ////////////////////////////////////////////////////////////////////////////// 55 | #define DRV_SYS_END (47 * 1024) //System defined safe end of driver's address space 56 | #define PCM_CTRL_MAX (93) 57 | ////////////////////////////////////////////////////////////////////////////// 58 | #define PCM_ALT_LOOP (3) 59 | #define PCM_RVS_LOOP (2) 60 | #define PCM_FWD_LOOP (1) 61 | #define PCM_VOLATILE (0) 62 | #define PCM_PROTECTED (-1) 63 | #define PCM_SEMI (-2) 64 | #define ADX_STREAM (-3) 65 | ////////////////////////////////////////////////////////////////////////////// 66 | #define PCM_TYPE_ADX (2) // 4-bit (compressed audio) 67 | #define PCM_TYPE_8BIT (1) // 8-bit 68 | #define PCM_TYPE_16BIT (0) // 16-bit 69 | ////////////////////////////////////////////////////////////////////////////// 70 | #define PCM_SYS_REGION (0) //0 for NTSC, 1 for PAL 71 | ////////////////////////////////////////////////////////////////////////////// 72 | #define PCM_PAN_LEFT (1<<4) 73 | #define PCM_PAN_RIGHT (0) 74 | ////////////////////////////////////////////////////////////////////////////// 75 | #define ADX_MASTER_768 (0) 76 | #define ADX_MASTER_1152 (1) 77 | #define ADX_MASTER_1536 (2) 78 | #define ADX_MASTER_2304 (3) 79 | /* 7.68 Data */ 80 | #define ADX_768_COEF_1 (4401) 81 | #define ADX_768_COEF_2 (-1183) 82 | /* 11.52 data */ 83 | #define ADX_1152_COEF_1 (5386) 84 | #define ADX_1152_COEF_2 (-1771) 85 | /* 15.36 data */ 86 | #define ADX_1536_COEF_1 (5972) 87 | #define ADX_1536_COEF_2 (-2187) 88 | /* 23.04 data */ 89 | #define ADX_2304_COEF_1 (6631) 90 | #define ADX_2304_COEF_2 (-2685) 91 | ////////////////////////////////////////////////////////////////////////////// 92 | // 6400 Hz for PAL 93 | #define ADX_PAL_640 (4) 94 | #define ADX_640_COEF_1 (3915) 95 | #define ADX_640_COEF_2 (-936) 96 | // 9600 Hz for PAL 97 | #define ADX_PAL_960 (5) 98 | #define ADX_960_COEF_1 (4963) 99 | #define ADX_960_COEF_2 (-1504) 100 | // 12800 Hz for PAL 101 | #define ADX_PAL_1280 (6) 102 | #define ADX_1280_COEF_1 (5612) 103 | #define ADX_1280_COEF_2 (-1923) 104 | // 19200 Hz for PAL 105 | #define ADX_PAL_1920 (7) 106 | #define ADX_1920_COEF_1 (6359) 107 | #define ADX_1920_COEF_2 (-2469) 108 | 109 | 110 | typedef struct { 111 | char loopType; //[0,1,2,3] No loop, normal loop, reverse loop, alternating loop 112 | unsigned char bitDepth; //0, 1, or 2; 0 is 16-bit, 1 is 8-bit, 2 is ADX 113 | unsigned short hiAddrBits; //bits 19-16 of... 114 | unsigned short loAddrBits; //Two 16-bit chunks that when combined, form the start address of the sound. 115 | unsigned short LSA; //The # of samples forward from the start address to return to after loop. 116 | unsigned short playsize; //The # of samples to play before the sound shall loop. **Otherwise used as the length of the sound.** Do not leave at 0! 117 | //8 bit PCM is 1 byte per sample. 16 bit PCM is 2 bytes per sample. Therefore an 8bit PCM is a maximum of 64KB, and 16bit is 128KB. 118 | unsigned short pitchword; //the OCT & FNS word to use in the ICSR, verbatim. 119 | unsigned char pan; //Direct pan setting 120 | unsigned char volume; //Direct volume setting 121 | unsigned short bytes_per_blank; //Bytes the PCM will play every time the driver is run (vblank) 122 | unsigned short decompression_size; //Size of the buffer used for an ADX sound effect. Specifically sized by Master SH2. 123 | unsigned char sh2_permit; //Does the SH2 permit this command? If TRUE, run the command. If FALSE, key its ICSR OFF. 124 | char icsr_target; //Which explicit ICSR is this to land in? Can be controlled by SH2 or by driver. 125 | } _PCM_CTRL; //Driver Local Command Struct 126 | 127 | typedef struct { 128 | volatile unsigned int adx_stream_length; //Length of the ADX stream (in ADX frames) 129 | volatile unsigned short start; //System Start Boolean 130 | volatile char adx_buffer_pass[2]; //Booleans 131 | volatile short drv_adx_coef_1; //The (signed!) coefficient 1 the driver will use to build ADX multiplication tables. 132 | volatile short drv_adx_coef_2; //The (signed!) coefficient 2 the driver will use to build ADX multiplication tables. 133 | volatile _PCM_CTRL * pcmCtrl; 134 | volatile unsigned char cdda_left_channel_vol_pan; // Redbook left channel volume & pan. 135 | volatile unsigned char cdda_right_channel_vol_pan; // Redbook right channel volume & pan. 136 | } sysComPara; 137 | 138 | 139 | typedef struct { 140 | unsigned short one_half; //[this is 32768 or 0x8000] 141 | short offset2data; 142 | unsigned char format; //[this is 3] 143 | unsigned char block_size; //[this is 18] 144 | unsigned char bit_depth; //[this is 4] 145 | unsigned char channels; //[this should be 1] 146 | unsigned int sample_rate; 147 | unsigned int sample_ct; 148 | unsigned short hp_cutoff; //[this should be 500] 149 | unsigned char loop; 150 | unsigned char illegal; //[Boolean, 0 for false, 1 for true] 151 | } adx_header; 152 | 153 | // 154 | 155 | // 156 | extern sysComPara * m68k_com; 157 | extern unsigned int * scsp_load; 158 | extern unsigned short * master_volume; 159 | extern unsigned short driver_master_volume; 160 | extern short numberPCMs; 161 | 162 | // 163 | // System functions shared for pcmstm.c/h 164 | // 165 | short convert_bitrate_to_pitchword(short sampleRate); 166 | short calculate_bytes_per_blank(int sampleRate, Bool is8Bit, Bool isPAL); 167 | short lcm(short a, short b); 168 | void cd_init(void); 169 | 170 | // 171 | // These are likely to be duplicate commands from other libraries. 172 | // 173 | void smpc_wait_till_ready(void); 174 | void smpc_issue_command(unsigned char cmd); 175 | // 176 | // 177 | // 178 | 179 | short load_16bit_pcm(Sint8 * filename, int sampleRate); 180 | short load_8bit_pcm(Sint8 * filename, int sampleRate); 181 | short load_adx(Sint8 * filename); 182 | void load_drv(int master_adx_frequency); 183 | 184 | void set_master_volume(unsigned short volume); 185 | void pcm_play(short pcmNumber, char ctrlType, char volume); 186 | void pcm_parameter_change(short pcmNumber, char volume, char pan); 187 | void pcm_cease(short pcmNumber); 188 | 189 | // 190 | // Usage: 191 | // Intended as the "level reset" function. 192 | // Does not soft or hard reset driver. To do that, re-load the driver binary (run load_drv again). 193 | // This instead resets the loading pointer and number of PCMs to a specific PCM number. 194 | // In use with proper sequence of asset loading, a certain number of sound assets can be retained in sound memory, with others discarded. 195 | // 196 | // The argument "highest_pcm_number_to_keep" is the latest sequentially loaded PCM in sound RAM that signals the point at which: 197 | // Any PCM number loaded earlier than this will be kept in memory and its number still valid to play the sound. 198 | // Any PCM number loaded later than this will be ignored in memory when loading new sounds, but the number is still valid to play sound. 199 | void pcm_reset(short default_pcm_count); 200 | 201 | void sdrv_vblank_rq(void); 202 | 203 | // 204 | // Redbook support 205 | // Credit: ndiddy, ReyeMe, CyberWarriorX [Iapetus] 206 | // 207 | 208 | // ------------------------------------- 209 | // Types 210 | // ------------------------------------- 211 | 212 | /** @brief Track location data 213 | */ 214 | typedef struct 215 | { 216 | unsigned int Control:4; 217 | unsigned int Number:4; 218 | unsigned int fad:24; 219 | } CDTrackLocation; 220 | 221 | /** @brief Track information data 222 | */ 223 | typedef struct 224 | { 225 | unsigned char Control:4; 226 | unsigned char Address:4; 227 | unsigned char Number; 228 | union { 229 | short point; 230 | struct { 231 | char psec; 232 | char pframe; 233 | } pData; 234 | 235 | }pBody; 236 | 237 | } CDTrackInformation; 238 | 239 | /** @brief Session data 240 | */ 241 | typedef struct 242 | { 243 | unsigned int Control:4; 244 | unsigned int Address:4; 245 | unsigned int fad:24; 246 | } CDSession; 247 | 248 | /** @brief Table of contents 249 | */ 250 | #define MAX_CD_TRACK_COUNT (24) 251 | typedef struct 252 | { 253 | CDTrackLocation Tracks[MAX_CD_TRACK_COUNT]; 254 | CDTrackInformation FirstTrack; 255 | CDTrackInformation LastTrack; 256 | CDSession Session; 257 | } CDTableOfContents; 258 | 259 | 260 | void CDDA_SetVolume(int vol); 261 | void CDDA_SetChannelVolPan(unsigned char left_channel, unsigned char right_channel); 262 | void CDDA_Play(int fromTrack, int toTrack, Bool loop, int startAddress); 263 | void CDDA_PlaySingle(int track, Bool loop); 264 | int CDDA_Stop(void); 265 | 266 | 267 | #endif 268 | 269 | -------------------------------------------------------------------------------- /jo_cdda_demo/run_with_daemon_tools_and_ssf.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | 3 | SET EMULATOR_DIR=..\..\Emulators 4 | SET DT_DIR=C:\Program Files (x86)\DAEMON Tools Lite 5 | 6 | if exist game.iso ( 7 | echo Mounting image... 8 | "%DT_DIR%\DTLite.exe" -mount 0,game.cue 9 | cd "%EMULATOR_DIR%\SSF\" 10 | echo Running SSF... 11 | "SSF.exe" 12 | echo Unmounting image... 13 | "%DT_DIR%\DTLite.exe" -unmount 0 14 | ) else ( 15 | echo Please compile first ! 16 | pause 17 | ) 18 | -------------------------------------------------------------------------------- /jo_cdda_demo/run_with_mednafen.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET MEDNAFEN_EXECUTABLE_PATH=%EMULATOR_DIR%\mednafen\mednafen.exe 4 | 5 | if not exist %MEDNAFEN_EXECUTABLE_PATH% ( 6 | echo --- 7 | echo Please install Mednafen here %EMULATOR_DIR% 8 | echo --- 9 | pause 10 | exit 11 | ) 12 | 13 | if exist game.cue ( 14 | "%MEDNAFEN_EXECUTABLE_PATH%" "%cd%\game.cue" -sound.volume "150" 15 | ) else ( 16 | echo Please compile first ! 17 | ) 18 | -------------------------------------------------------------------------------- /jo_cdda_demo/run_with_nova.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET NOVA_BIOS_PATH=%EMULATOR_DIR%\nova\bios\bios.bin 4 | 5 | if not exist %NOVA_BIOS_PATH% ( 6 | echo --- 7 | echo Nova doesn't support HLE bios today. 8 | echo Therefore, please put Sega Saturn bios here %NOVA_BIOS_PATH% 9 | echo --- 10 | pause 11 | exit 12 | ) 13 | 14 | if exist game.iso ( 15 | "%EMULATOR_DIR%\nova\nova.exe" "%cd%\game.cue" 16 | ) else ( 17 | echo Please compile first ! 18 | ) 19 | -------------------------------------------------------------------------------- /jo_cdda_demo/run_with_powershell_and_ssf.ps1: -------------------------------------------------------------------------------- 1 | # /!\ THIS SCRIPT DOESN'T WORK TODAY BECAUSE OF SSF /!\ 2 | $isoPath = Join-Path $pwd.Path game.iso 3 | $ssfDirectory = Join-Path $pwd.Path "..\..\Emulators\SSF\" 4 | Write-Host "Mounting image..." 5 | Mount-DiskImage -StorageType ISO -ImagePath $isoPath 6 | Write-Host "Running SSF..." 7 | cd "$ssfDirectory" 8 | Start-Process -FilePath "SSF.exe" -Wait 9 | Write-Host "Unmounting image..." 10 | Get-DiskImage $isoPath | Dismount-DiskImage 11 | -------------------------------------------------------------------------------- /jo_cdda_demo/run_with_virtual_clone_drive_and_ssf.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET VCD_DIR=C:\Program Files (x86)\Elaborate Bytes\VirtualCloneDrive 4 | 5 | if exist game.iso ( 6 | echo Mounting image... 7 | "%VCD_DIR%\vcdmount.exe" game.iso 8 | cd "%EMULATOR_DIR%\SSF\" 9 | echo Running SSF... 10 | "SSF.exe" 11 | echo Unmounting image... 12 | "%VCD_DIR%\vcdmount.exe" /u 13 | ) else ( 14 | echo Please compile first ! 15 | pause 16 | ) 17 | -------------------------------------------------------------------------------- /jo_cdda_demo/run_with_yabaSanshiro.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | 4 | if exist game.iso ( 5 | echo Please wait, it's very slow, but I don't know why... 6 | "%EMULATOR_DIR%\YabaSanshiro\yabasanshiro.exe" -a -i game.iso 7 | ) else ( 8 | echo Please compile first ! 9 | ) 10 | -------------------------------------------------------------------------------- /jo_cdda_demo/run_with_yabause.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | 4 | if exist game.iso ( 5 | "%EMULATOR_DIR%\yabause\yabause.exe" -a -i game.iso 6 | ) else ( 7 | echo Please compile first ! 8 | ) 9 | -------------------------------------------------------------------------------- /jo_cdda_demo/run_with_yabause.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | command -v yabause >/dev/null 2>&1 || { echo "yabause is not installed.\ 3 | Aborting." >&2; exit 1; } 4 | 5 | if [ -f game.iso ]; 6 | then 7 | yabause -a -i game.cue 8 | else 9 | echo "Please compile first !" >&2 10 | fi 11 | -------------------------------------------------------------------------------- /jo_cdda_demo/use_mednafen.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | 3 | if exist game.iso ( 4 | "D:\games\med\mednafen.exe" game.cue 5 | ) else ( 6 | echo Please compile first ! 7 | ) -------------------------------------------------------------------------------- /jo_demo/cd/ABS.TXT: -------------------------------------------------------------------------------- 1 | NOT Abstracted by SEGA 2 | 3 | -------------------------------------------------------------------------------- /jo_demo/cd/BIB.TXT: -------------------------------------------------------------------------------- 1 | NOT Bibliographiced by SEGA 2 | 3 | -------------------------------------------------------------------------------- /jo_demo/cd/CPY.TXT: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /jo_demo/cd/Nbgm.ADX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_demo/cd/Nbgm.ADX -------------------------------------------------------------------------------- /jo_demo/cd/def_flag.PCM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_demo/cd/def_flag.PCM -------------------------------------------------------------------------------- /jo_demo/cd/hav_flag.PCM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_demo/cd/hav_flag.PCM -------------------------------------------------------------------------------- /jo_demo/cd/sdrv.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_demo/cd/sdrv.bin -------------------------------------------------------------------------------- /jo_demo/clean.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET COMPILER_DIR=..\..\Compiler 3 | SET JO_ENGINE_SRC_DIR=../../jo_engine 4 | SET PATH=%COMPILER_DIR%\WINDOWS\Other Utilities;%PATH% 5 | 6 | rm -f ./cd/0.bin 7 | rm -f *.o 8 | rm -f %JO_ENGINE_SRC_DIR%/*.o 9 | rm -f ./*.bin 10 | rm -f ./*.coff 11 | rm -f ./*.elf 12 | rm -f ./*.map 13 | rm -f ./*.iso 14 | rm -f ./*.cue 15 | 16 | ECHO Done. 17 | -------------------------------------------------------------------------------- /jo_demo/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f ./cd/0.bin 3 | rm -f *.o 4 | rm -f ../../jo_engine/*.o 5 | rm -f ./*.bin 6 | rm -f ./*.coff 7 | rm -f ./*.elf 8 | rm -f ./*.map 9 | rm -f ./*.iso 10 | rm -f ./*.cue 11 | echo "Done." 12 | exit 0 13 | -------------------------------------------------------------------------------- /jo_demo/compile.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET COMPILER_DIR=..\..\Compiler 3 | SET PATH=%COMPILER_DIR%\WINDOWS\Other Utilities;%COMPILER_DIR%\WINDOWS\bin;%PATH% 4 | make re 5 | PAUSE -------------------------------------------------------------------------------- /jo_demo/compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | export NCPU=`nproc` 3 | make clean && make -j${NCPU} all 4 | exit 0 5 | 6 | -------------------------------------------------------------------------------- /jo_demo/main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "pcmsys.h" 4 | 5 | int framerate; 6 | 7 | //Sound Numbers 8 | short pcm8snd; 9 | short pcm16snd; 10 | short adx4snd; 11 | // 12 | 13 | // 14 | // UP NEXT: Multi-PCM Stream 15 | // 16 | 17 | void my_draw(void) 18 | { 19 | jo_printf(0, 5, "P64 PCM Driver Usage Demo"); 20 | jo_printf(0, 8, "A to start an ADX semi-protected sound"); 21 | jo_printf(0, 9, "(will only start this sound type)"); 22 | jo_printf(0, 10, "(will restart whenever told to play)"); 23 | jo_printf(0, 12, "(Hold B to start a 16-bit alt-looping sound."); 24 | jo_printf(0, 13, "Release to stop the sound.)"); 25 | jo_printf(0, 16, "Hold C to repeat an 8-bit protected sound"); 26 | jo_printf(0, 17, "(this sound type plays while true)"); 27 | jo_printf(0, 18, "(and will only restart when done)"); 28 | 29 | jo_printf(0, 20, "The ADX bitrate in this demo is 23040."); 30 | jo_printf(0, 21, "23040, 15360, 11520, or 7680Hz can be used."); 31 | jo_printf(0, 22, "ADX decompression is done in real-time."); 32 | jo_printf(0, 23, "The driver may only properly decompress one"); 33 | jo_printf(0, 24, "bitrate of ADX, selected at startup."); 34 | 35 | // jo_printf(0, 18, "Y pans sound from A button"); 36 | 37 | if(jo_is_input_key_down(0, JO_KEY_A)) 38 | { 39 | pcm_play(adx4snd, PCM_SEMI, 7); 40 | } 41 | 42 | if(jo_is_input_key_pressed(0, JO_KEY_B)) 43 | { 44 | jo_printf(0, 14, "(--Loop Play--)"); 45 | pcm_play(pcm16snd, PCM_ALT_LOOP, 6); 46 | } else { 47 | jo_printf(0, 14, "(--Loop Stop--)"); 48 | pcm_cease(pcm16snd); 49 | } 50 | 51 | if(jo_is_input_key_pressed(0, JO_KEY_C)) 52 | { 53 | pcm_play(pcm8snd, PCM_PROTECTED, 6); 54 | } 55 | 56 | //slSynch(); 57 | } 58 | 59 | void jo_main(void) 60 | { 61 | jo_core_init(JO_COLOR_Black); 62 | 63 | load_drv(ADX_MASTER_2304); 64 | //////////////////////////////////////////////// 65 | // REMINDER: All file names must comply with the 8.3 standard. 66 | // File extensions can be no longer than 3 letters. 67 | // File names can be no longer than 8 letters. 68 | // The total length is thusly 12 characters (as there is a period). 69 | //////////////////////////////////////////////// 70 | /* 71 | To convert a sound to 16-bit 72 | ffmpeg -i %this%.wav -f s16be -ac 1 -ar (bitrate) %this%.PCM 73 | To convert a sound to 8-bit 74 | ffmpeg -i %this%.wav -f s8 -ac 1 -ar (bitrate) %this%.PCM 75 | */ 76 | pcm16snd = load_16bit_pcm((Sint8 *)"DEF_FLAG.PCM", 15360); 77 | pcm8snd = load_8bit_pcm((Sint8 *)"HAV_FLAG.PCM", 15360); 78 | adx4snd = load_adx((Sint8 *)"NBGM.ADX"); 79 | 80 | 81 | jo_core_add_vblank_callback(sdrv_vblank_rq); 82 | jo_core_add_callback(my_draw); 83 | jo_core_run(); 84 | } 85 | 86 | /* 87 | ** END OF FILE 88 | */ 89 | 90 | -------------------------------------------------------------------------------- /jo_demo/makefile: -------------------------------------------------------------------------------- 1 | JO_COMPILE_WITH_VIDEO_MODULE = 1 2 | JO_COMPILE_WITH_BACKUP_MODULE = 0 3 | JO_COMPILE_WITH_TGA_MODULE = 0 4 | JO_COMPILE_WITH_AUDIO_MODULE = 0 5 | JO_COMPILE_WITH_3D_MODULE = 0 6 | JO_COMPILE_WITH_PSEUDO_MODE7_MODULE = 0 7 | JO_COMPILE_WITH_EFFECTS_MODULE = 0 8 | JO_COMPILE_WITH_FS_MODULE = 0 9 | JO_PSEUDO_SATURN_KAI_SUPPORT = 1 10 | JO_COMPILE_WITH_DUAL_CPU_MODULE = 0 11 | JO_DEBUG = 0 12 | JO_COMPILE_USING_SGL=1 13 | JO_NTSC = 1 14 | SRCS=main.c pcmsys.c 15 | JO_ENGINE_SRC_DIR=../../jo_engine 16 | COMPILER_DIR=../../Compiler 17 | include $(COMPILER_DIR)/COMMON/jo_engine_makefile 18 | -------------------------------------------------------------------------------- /jo_demo/pcmsys.c: -------------------------------------------------------------------------------- 1 | //pcm_sys.c 2 | //this file is compiled separately 3 | //hopefully somewhat portable 4 | // 5 | #include //Mostly to link us with SBL file system 6 | #include "pcmsys.h" 7 | #include 8 | #define true (1) 9 | #define false (0) 10 | 11 | static const int logtbl[] = { 12 | /* 0 */ 0, 13 | /* 1 */ 1, 14 | /* 2 */ 2, 2, 15 | /* 4 */ 3, 3, 3, 3, 16 | /* 8 */ 4, 4, 4, 4, 4, 4, 4, 4, 17 | /* 16 */ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 18 | /* 32 */ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 19 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 20 | /* 64 */ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 24 | /* 128 */ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 25 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 26 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 28 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 29 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 30 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 31 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 32 | }; 33 | 34 | #define PCM_MSK1(a) ((a)&0x0001) 35 | #define PCM_MSK3(a) ((a)&0x0007) 36 | #define PCM_MSK4(a) ((a)&0x000F) 37 | #define PCM_MSK5(a) ((a)&0x001F) 38 | #define PCM_MSK10(a) ((a)&0x03FF) 39 | 40 | #define PCM_SCSP_FREQUENCY (44100L) 41 | 42 | #define PCM_CALC_OCT(smpling_rate) \ 43 | ((int)logtbl[PCM_SCSP_FREQUENCY / ((smpling_rate) + 1)]) 44 | 45 | #define PCM_CALC_SHIFT_FREQ(oct) \ 46 | (PCM_SCSP_FREQUENCY >> (oct)) 47 | 48 | #define PCM_CALC_FNS(smpling_rate, shift_freq) \ 49 | ((((smpling_rate) - (shift_freq)) << 10) / (shift_freq)) 50 | 51 | #define PCM_SET_PITCH_WORD(oct, fns) \ 52 | ((int)((PCM_MSK4(-(oct)) << 11) | PCM_MSK10(fns))) 53 | 54 | sysComPara * m68k_com = (sysComPara *)((SNDPRG + DRV_SYS_END) | 0x20000000); 55 | static unsigned int * scsp_loading_start = (unsigned int*)(0x408 + DRV_SYS_END + 0x20); //Local loading address for sound data, is DRV_SYS_END ahead of the SNDPRG, and ahead of the communication data 56 | unsigned int * scsp_load; 57 | unsigned short * master_volume = (unsigned short *)(SNDRAM + 0x100400); 58 | unsigned short driver_master_volume = 0; 59 | short numberPCMs = 0; 60 | 61 | static short adx_coef_tbl[8][2] = 62 | { 63 | {ADX_768_COEF_1, ADX_768_COEF_2}, 64 | {ADX_1152_COEF_1, ADX_1152_COEF_2}, 65 | {ADX_1536_COEF_1, ADX_1536_COEF_2}, 66 | {ADX_2304_COEF_1, ADX_2304_COEF_2}, 67 | {ADX_640_COEF_1, ADX_640_COEF_2}, 68 | {ADX_960_COEF_1, ADX_960_COEF_2}, 69 | {ADX_1280_COEF_1, ADX_1280_COEF_2}, 70 | {ADX_1920_COEF_1, ADX_1920_COEF_2} 71 | }; 72 | 73 | //MVOL is a 4-bit number; values 0-15 are valid. 74 | //If the value is higher than 15, this will just cap it at 15. 75 | void set_master_volume(unsigned short volume) 76 | { 77 | volume = (volume >= 0xF) ? 0xF : volume; 78 | *master_volume = 0x200 | (volume & 0xF); 79 | driver_master_volume = volume; 80 | } 81 | 82 | void pcm_play(short pcmNumber, char ctrlType, char volume) 83 | { 84 | if(pcmNumber < 0) return; 85 | m68k_com->pcmCtrl[pcmNumber].sh2_permit = 1; 86 | m68k_com->pcmCtrl[pcmNumber].volume = volume; 87 | m68k_com->pcmCtrl[pcmNumber].loopType = ctrlType; 88 | } 89 | 90 | void pcm_parameter_change(short pcmNumber, char volume, char pan) 91 | { 92 | if(pcmNumber < 0) return; 93 | m68k_com->pcmCtrl[pcmNumber].volume = volume; 94 | m68k_com->pcmCtrl[pcmNumber].pan = pan; 95 | } 96 | 97 | void pcm_cease(short pcmNumber) 98 | { 99 | if(pcmNumber < 0) return; 100 | if(m68k_com->pcmCtrl[pcmNumber].loopType <= 0) //If it is a volatile or protected sound, the expected control method is to mute the sound and let it end itself. 101 | { //Protected sounds have a permission state of "until they end". 102 | m68k_com->pcmCtrl[pcmNumber].volume = 0; 103 | } else { 104 | m68k_com->pcmCtrl[pcmNumber].sh2_permit = 0; //If it is a looping sound, the control method is to command it to stop. 105 | } 106 | } 107 | 108 | // 109 | // Usage: 110 | // Intended as the "level reset" function. 111 | // Does not soft or hard reset driver. To do that, re-load the driver binary (run load_drv again). 112 | // This instead resets the loading pointer and number of PCMs to a specific PCM number. 113 | // In use with proper sequence of asset loading, a certain number of sound assets can be retained in sound memory, with others discarded. 114 | // 115 | // The argument "highest_pcm_number_to_keep" is the latest sequentially loaded PCM in sound RAM that signals the point at which: 116 | // Any PCM number loaded earlier than this will be kept in memory and its number still valid to play the sound. 117 | // Any PCM number loaded later than this will be ignored in memory when loading new sounds, but the number is still valid to play sound. 118 | // Pass -1 to this function to clear all PCMs. 119 | void pcm_reset(short highest_pcm_number_to_keep) 120 | { 121 | //For clearing all sounds, input is negative 122 | if(highest_pcm_number_to_keep < 0) { 123 | scsp_load = scsp_loading_start; 124 | numberPCMs = 0; 125 | return; 126 | } 127 | 128 | numberPCMs = highest_pcm_number_to_keep+1; 129 | scsp_load = (unsigned int *)((unsigned int)(m68k_com->pcmCtrl[highest_pcm_number_to_keep].hiAddrBits<<16) | (int)(m68k_com->pcmCtrl[highest_pcm_number_to_keep].loAddrBits)); 130 | if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 2) 131 | { //If this is an ADX sound, offset the loading pointer by # of frames by 18. Address includes 18-byte header offset. 132 | scsp_load = (unsigned int *)((unsigned int)scsp_load + (m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize * 18)); 133 | } else if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 1) 134 | { //If this is an 8-bit PCM, offset the loading pointer by the playsize, exactly (one byte samples). 135 | scsp_load = (unsigned int *)((unsigned int)scsp_load + m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize); 136 | } else if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 0) 137 | { //If this is a 16-bit PCM, offset the loading pointer by the playsize, shifted left once (two byte samples). 138 | scsp_load = (unsigned int *)((unsigned int)scsp_load + (m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize<<1)); 139 | } 140 | } 141 | 142 | /**stolen from xl2**/ 143 | #define OPEN_MAX (Sint32)5 144 | #define DIR_MAX (Sint32)1024 145 | #define RD_UNIT (10) 146 | #define SECT_SIZE (2048) 147 | GfsDirTbl gfsDirTbl; 148 | GfsDirName gfsDirName[DIR_MAX]; 149 | Uint32 gfsLibWork[GFS_WORK_SIZE(OPEN_MAX)/sizeof(Uint32)]; 150 | Sint32 gfsDirN; 151 | void cd_init(void) 152 | { 153 | GFS_DIRTBL_TYPE(&gfsDirTbl) = GFS_DIR_NAME; 154 | GFS_DIRTBL_DIRNAME(&gfsDirTbl) = gfsDirName; 155 | GFS_DIRTBL_NDIR(&gfsDirTbl) = DIR_MAX; 156 | gfsDirN = GFS_Init(OPEN_MAX, gfsLibWork, &gfsDirTbl); 157 | } 158 | 159 | void smpc_wait_till_ready (void) 160 | { 161 | // Wait until SF register is cleared 162 | while(SMPC_REG_SF & 0x1) { } 163 | } 164 | 165 | ////////////////////////////////////////////////////////////////////////////// 166 | 167 | void smpc_issue_command(unsigned char cmd) 168 | { 169 | // Set SF register so that no other command can be issued. 170 | SMPC_REG_SF = 1; 171 | 172 | // Writing COMREG starts execution 173 | SMPC_REG_COMREG = cmd; 174 | } 175 | 176 | ////////////////////////////////////////////////////////////////////////////// 177 | 178 | void load_driver_binary(Sint8 * filename, void * buffer, int master_adx_frequency) 179 | { 180 | cd_init(); 181 | GfsHn s_gfs; 182 | Sint32 file_size; 183 | 184 | Sint32 local_name = GFS_NameToId(filename); 185 | 186 | //Open GFS 187 | s_gfs = GFS_Open((Sint32)local_name); 188 | //Get file information (mostly, the file size) 189 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 190 | 191 | GFS_Close(s_gfs); 192 | 193 | GFS_Load(local_name, 0, (Uint32 *)buffer, file_size); 194 | 195 | //The immediacy of these commands is important. 196 | //As per SEGA technical bulletin 51, the Sound CPU is not to be turned off for more than 0.5 seconds. 197 | // Turn off Sound CPU 198 | smpc_issue_command(SMPC_CMD_SNDOFF); 199 | smpc_wait_till_ready(); 200 | //Set max master volume + 4mbit memory 201 | set_master_volume(0xF); 202 | //Copy the driver binary (code) over to sound RAM. The binary includes the vector table information. 203 | slDMACopy(buffer, (void*)SNDRAM, file_size); 204 | slDMAWait(); 205 | //Set the ADX coefficients for the driver to use, if one was selected. 206 | 207 | m68k_com->drv_adx_coef_1 = adx_coef_tbl[master_adx_frequency][0]; 208 | m68k_com->drv_adx_coef_2 = adx_coef_tbl[master_adx_frequency][1]; 209 | 210 | // Turn on Sound CPU again 211 | smpc_wait_till_ready(); 212 | smpc_issue_command(SMPC_CMD_SNDON); 213 | // 214 | } 215 | 216 | void load_drv(int master_adx_frequency) 217 | { 218 | // Make sure SCSP is set to 512k mode 219 | *(unsigned char *)(0x25B00400) = 0x02; 220 | 221 | // Clear Sound Ram 222 | for (int i = 0; i < 0x80000; i+=4){ 223 | *(unsigned int *)(SNDRAM + i) = 0x00000000; 224 | } 225 | // Volatile loading buffer 226 | // This memory is not permanently used, its just a place to temporarily store the driver binary 227 | void * binary_buffer = (void*)2097152; 228 | 229 | // Copy driver over 230 | load_driver_binary((Sint8*)"SDRV.BIN", binary_buffer, master_adx_frequency); 231 | m68k_com->start = 0xFFFF; 232 | volatile int i = 0; 233 | scsp_load = scsp_loading_start; // Re-set loading pointer. 234 | for(i = 0; i < (int)scsp_load; i++) 235 | { 236 | //This is to pop the stack here. Because GCC. 237 | } 238 | //Additionally, reset the number of PCMs. 239 | numberPCMs = 0; 240 | } 241 | 242 | short calculate_bytes_per_blank(int sampleRate, Bool is8Bit, Bool isPAL) 243 | { 244 | int frameCount = (isPAL == true) ? 50 : 60; 245 | int sampleSize = (is8Bit == true) ? 8 : 16; 246 | return ((sampleRate * sampleSize)>>3)/frameCount; 247 | 248 | } 249 | 250 | short convert_bitrate_to_pitchword(short sampleRate) 251 | { 252 | 253 | int octr; 254 | int shiftr; 255 | int fnsr; 256 | 257 | octr = PCM_CALC_OCT(sampleRate); 258 | shiftr = PCM_CALC_SHIFT_FREQ(octr); 259 | fnsr = PCM_CALC_FNS(sampleRate, shiftr); 260 | 261 | return PCM_SET_PITCH_WORD(octr, fnsr); 262 | } 263 | 264 | short load_16bit_pcm(Sint8 * filename, int sampleRate) 265 | { 266 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 267 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 268 | 269 | GfsHn s_gfs; 270 | Sint32 file_size; 271 | 272 | Sint32 local_name = GFS_NameToId(filename); 273 | 274 | //Open GFS 275 | s_gfs = GFS_Open((Sint32)local_name); 276 | //Get file information (mostly, the file size) 277 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 278 | 279 | GFS_Close(s_gfs); 280 | 281 | if(file_size > (128 * 1024)) return -1; //PCM size too large for general-purpose playback [could still work with timed execution & offets] 282 | 283 | file_size += ((unsigned int)file_size & 1) ? 1 : 0; 284 | file_size += ((unsigned int)file_size & 3) ? 2 : 0; 285 | 286 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), file_size); 287 | 288 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)scsp_load >> 16); 289 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)scsp_load & 0xFFFF); 290 | 291 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(sampleRate); 292 | m68k_com->pcmCtrl[numberPCMs].playsize = (file_size>>1); 293 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = calculate_bytes_per_blank(sampleRate, false, PCM_SYS_REGION); //Iniitalize as max volume 294 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_16BIT; //Select 16-bit 295 | m68k_com->pcmCtrl[numberPCMs].loopType = 0; //Initialize as non-looping 296 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Initialize as max volume 297 | 298 | 299 | numberPCMs++; //Increment pcm # 300 | scsp_load = (unsigned int *)((unsigned int )scsp_load + file_size); 301 | return (numberPCMs-1); //Return the PCM # this sound recieved 302 | } 303 | 304 | short load_8bit_pcm(Sint8 * filename, int sampleRate) 305 | { 306 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 307 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 308 | 309 | GfsHn s_gfs; 310 | Sint32 file_size; 311 | 312 | Sint32 local_name = GFS_NameToId(filename); 313 | 314 | //Open GFS 315 | s_gfs = GFS_Open((Sint32)local_name); 316 | //Get file information (mostly, the file size) 317 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 318 | 319 | GFS_Close(s_gfs); 320 | 321 | 322 | if(file_size > (64 * 1024)) return -1; //PCM size too large for general-purpose playback [could still work with timed execution & offets] 323 | 324 | 325 | file_size += ((unsigned int)file_size & 1) ? 1 : 0; 326 | file_size += ((unsigned int)file_size & 3) ? 2 : 0; 327 | 328 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), file_size); 329 | 330 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)scsp_load >> 16); 331 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)scsp_load & 0xFFFF); 332 | 333 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(sampleRate); 334 | m68k_com->pcmCtrl[numberPCMs].playsize = (file_size); 335 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = calculate_bytes_per_blank(sampleRate, true, PCM_SYS_REGION); //Iniitalize as max volume 336 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_8BIT; //Select 8-bit 337 | m68k_com->pcmCtrl[numberPCMs].loopType = 0; //Initialize as non-looping 338 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Iniitalize as max volume 339 | 340 | 341 | numberPCMs++; //Increment pcm # 342 | scsp_load = (unsigned int *)((unsigned int )scsp_load + file_size); 343 | return (numberPCMs-1); //Return the PCM # this sound received 344 | } 345 | 346 | // Recursive function to return gcd of a and b 347 | short gcd(short a, short b) 348 | { 349 | if (a == 0) 350 | return b; 351 | return gcd(b % a, a); 352 | } 353 | 354 | // Function to return LCM of two numbers 355 | // Used specifically to find the buffer size for ADX sound effects 356 | short lcm(short a, short b) 357 | { 358 | return (a / gcd(a, b)) * b; 359 | } 360 | 361 | short load_adx(Sint8 * filename) 362 | { 363 | static adx_header adx; 364 | 365 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 366 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 367 | 368 | Sint32 local_name = GFS_NameToId(filename); 369 | 370 | ////////////////////////// 371 | // Step 1: Load the size of the header from the file to the header's location 372 | ////////////////////////// 373 | GFS_Load(local_name, 0, (Uint32 *)&adx, sizeof(adx_header)); 374 | ////////////////////////// 375 | // Step 2: Check the data we just loaded and make sure it's an ADX file. 376 | // If the data does not match what the decompression routine expects, this function will return -1. 377 | ////////////////////////// 378 | // jo_printf(13, 9, "ohalf(%i)", adx.one_half); 379 | // jo_printf(13, 10, "blocksz(%i)", adx.block_size); 380 | // jo_printf(13, 11, "bitdepth(%i)", adx.bit_depth); 381 | // jo_printf(13, 12, "srate(%i)", adx.sample_rate); 382 | if(adx.one_half != 32768 || adx.block_size != 18 || adx.bit_depth != 4) return -1; 383 | ////////////////////////// 384 | // Step 3: Parse the data in the header to the sound driver PCM control struct. 385 | // Special things for ADX is the playsize is actually the # of ADX frames, not the number of samples. 386 | // We expect a block size of 16, so each frame has 16 bytes of sample data which is 32 samples. 387 | ////////////////////////// 388 | // Because we ""dumbly"" load the header to sound RAM, let's give the used pointer the offset. 389 | unsigned int working_address = (unsigned int)(scsp_load) + adx.offset2data + 4; 390 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)working_address >> 16); 391 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)working_address & 0xFFFF); 392 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(adx.sample_rate); 393 | m68k_com->pcmCtrl[numberPCMs].playsize = (adx.sample_ct / 32); 394 | short bpb = calculate_bytes_per_blank((int)adx.sample_rate, false, PCM_SYS_REGION); //Iniitalize as max volume 395 | if(bpb != 768 && bpb != 512 && bpb != 384 && bpb != 256 && bpb != 192 && bpb != 128) 396 | { 397 | slPrint("!(ADX INVALID BYTE-RATE)!", slLocate(0, 1)); 398 | return -2; 399 | } 400 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = bpb; 401 | unsigned short big_dct_sz = (bpb >= 256) ? lcm(bpb, bpb + 64)<<1 : 5376; // Dirty fix for ultra low bitrate 402 | m68k_com->pcmCtrl[numberPCMs].decompression_size = (big_dct_sz > (adx.sample_ct<<1)) ? adx.sample_ct<<1 : big_dct_sz; 403 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_ADX; //Select ADX type 404 | m68k_com->pcmCtrl[numberPCMs].loopType = PCM_SEMI; //Initialize as semi-protected. 405 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Iniitalize as max volume 406 | numberPCMs++; 407 | ///////////////////////// 408 | // Step 4: Load the compressed ADX data to sound RAM. Unfortunately, we must include the 20 byte header. 409 | // We want to load from an offset location from CD and then load (sample_ct / 32) * 18 bytes. 410 | // We divide the sample count by 32 to retrieve the ADX frame count. We multiply by 18, as that is the size of an ADX 'frame'. 411 | ///////////////////////// 412 | unsigned int number_of_bytes_to_load = (adx.sample_ct / 32) * 18; 413 | number_of_bytes_to_load += ((unsigned int)number_of_bytes_to_load & 1) ? 1 : 0; 414 | number_of_bytes_to_load += ((unsigned int)number_of_bytes_to_load & 3) ? 2 : 0; 415 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), number_of_bytes_to_load); 416 | scsp_load = (unsigned int *)((unsigned int )scsp_load + number_of_bytes_to_load); 417 | return (numberPCMs-1); //Return the PCM # this sound recieved 418 | } 419 | 420 | void sdrv_vblank_rq(void) 421 | { 422 | //jo_printf(0, 0, "drv_stat(%i)", m68k_com->start); 423 | m68k_com->start = 1; 424 | } 425 | 426 | //////////////////////////////////////////////////////////////////////////////// 427 | // Redbook Support 428 | // These are mostly CDC commands. 429 | // credit to: ndiddy, ReyeMe, CyberWarriorX [iapetus] 430 | //////////////////////////////////////////////////////////////////////////////// 431 | 432 | 433 | void CDDA_SetVolume(int vol) 434 | { 435 | //Step 1: Remove the volume bits from the value (isolate the pan) 436 | unsigned char newvol = m68k_com->cdda_left_channel_vol_pan & 0x1F; 437 | //Step 2: Apply the volume to the correct bits 438 | newvol |= ((vol & 0x7)<<5); 439 | //Step 3: Apply value back to left channel 440 | m68k_com->cdda_left_channel_vol_pan = newvol; 441 | //Step 4: Repeat for right channel 442 | newvol = m68k_com->cdda_right_channel_vol_pan & 0x1F; 443 | //Step 5: Apply the volume to the correct bits 444 | newvol |= ((vol & 0x7)<<5); 445 | //Step 6: Apply value back to right channel 446 | m68k_com->cdda_right_channel_vol_pan = newvol; 447 | } 448 | 449 | // To see what this does and how to use it, refer to the SCSP manual. 450 | // Warning: Use without reading the manual may break your CD audio. 451 | void CDDA_SetChannelVolPan(unsigned char left_channel, unsigned char right_channel) 452 | { 453 | m68k_com->cdda_left_channel_vol_pan = left_channel; 454 | m68k_com->cdda_right_channel_vol_pan = right_channel; 455 | } 456 | 457 | 458 | // ------------------------------------- 459 | // Functions 460 | // ------------------------------------- 461 | 462 | /** @brief Gets table of contents 463 | * @param toc Table of contents data struct 464 | */ 465 | void CDGetTableOfContents(CDTableOfContents * toc) 466 | { 467 | CDC_TgetToc((Uint32*)toc); 468 | } 469 | 470 | /** @brief Play audio 471 | * @param fromTrack Start track 472 | * @param toTrack End track 473 | * @param loop Loop playback 474 | * @param startAddress Start address of the playback 475 | */ 476 | void CDDA_Play(int fromTrack, int toTrack, Bool loop, int startAddress) 477 | { 478 | CdcPly ply; 479 | 480 | // Get TOC 481 | CDTableOfContents toc; 482 | CDGetTableOfContents(&toc); 483 | 484 | // Start of the playback address 485 | CDC_PLY_STYPE(&ply) = CDC_PTYPE_FAD; // track number 486 | 487 | if (startAddress == 0) 488 | { 489 | 490 | CDC_PLY_SFAD(&ply) = toc.Tracks[fromTrack].fad; 491 | } 492 | else 493 | { 494 | CDC_PLY_SFAD(&ply) = startAddress; 495 | } 496 | 497 | if (toTrack + 1 < MAX_CD_TRACK_COUNT) 498 | { 499 | // End of the playback address 500 | CDC_PLY_ETYPE(&ply) = CDC_PTYPE_FAD; 501 | CDC_PLY_EFAS(&ply) = toc.Tracks[toTrack + 1].fad - toc.Tracks[fromTrack].fad; 502 | } 503 | else 504 | { 505 | // End of the playback is end of the disk 506 | CDC_PLY_ETYPE(&ply) = CDC_PTYPE_DFL; 507 | } 508 | 509 | // Playback mode 510 | CDC_PLY_PMODE(&ply) = CDC_PM_DFL | (loop ? 0xf : 0); // 0xf = infinite repetitions 511 | 512 | // Start playback 513 | CDC_CdPlay(&ply); 514 | } 515 | 516 | /** @brief Stop audio playback 517 | * @return Position of where playback stopped 518 | */ 519 | int CDDA_Stop(void) 520 | { 521 | // Get current address 522 | CdcStat stat; 523 | CDC_GetCurStat(&stat); 524 | 525 | // Restore address 526 | CdcPos poswk; 527 | CDC_POS_PTYPE(&poswk) = CDC_PTYPE_DFL; 528 | CDC_CdSeek(&poswk); 529 | 530 | // Last playback address 531 | return stat.report.fad; 532 | } 533 | 534 | void CDDA_PlaySingle(int track, Bool loop) 535 | { 536 | CDDA_Play(track, track, loop, 0); 537 | } 538 | 539 | 540 | -------------------------------------------------------------------------------- /jo_demo/pcmsys.h: -------------------------------------------------------------------------------- 1 | // 2 | //pcmsys.h 3 | // 4 | #ifndef __PCMSYS_H__ 5 | # define __PCMSYS_H__ 6 | 7 | /////////////// 8 | // Likely duplicates from other libraries (in this case, taken from iapetus) 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #define SMPC_REG_IREG(i) *((volatile unsigned char *)0x20100001+((i) * 2)) 12 | #define SMPC_REG_COMREG *((volatile unsigned char *)0x2010001F) 13 | #define SMPC_REG_OREG(o) *((volatile unsigned char *)0x20100021+((o) * 2)) 14 | #define SMPC_REG_SR *((volatile unsigned char *)0x20100061) 15 | #define SMPC_REG_SF *((volatile unsigned char *)0x20100063) 16 | #define SMPC_REG_PDR1 *((volatile unsigned char *)0x20100075) 17 | #define SMPC_REG_PDR2 *((volatile unsigned char *)0x20100077) 18 | #define SMPC_REG_DDR1 *((volatile unsigned char *)0x20100079) 19 | #define SMPC_REG_DDR2 *((volatile unsigned char *)0x2010007B) 20 | #define SMPC_REG_IOSEL *((volatile unsigned char *)0x2010007D) 21 | #define SMPC_REG_EXLE *((volatile unsigned char *)0x2010007F) 22 | 23 | ////////////////////////////////////////////////////////////////////////////// 24 | 25 | #define SMPC_CMD_MSHON 0x00 26 | #define SMPC_CMD_SSHON 0x02 27 | #define SMPC_CMD_SSHOFF 0x03 28 | #define SMPC_CMD_SNDON 0x06 29 | #define SMPC_CMD_SNDOFF 0x07 30 | #define SMPC_CMD_CDON 0x08 31 | #define SMPC_CMD_CDOFF 0x09 32 | #define SMPC_CMD_CARTON 0x0A 33 | #define SMPC_CMD_CARTOFF 0x0B 34 | #define SMPC_CMD_SYSRES 0x0D 35 | #define SMPC_CMD_CKCHG352 0x0E 36 | #define SMPC_CMD_CKCHG320 0x0F 37 | #define SMPC_CMD_INTBACK 0x10 38 | #define SMPC_CMD_SETTIM 0x16 39 | #define SMPC_CMD_SETSM 0x17 40 | #define SMPC_CMD_NMIREQ 0x18 41 | #define SMPC_CMD_RESENA 0x19 42 | #define SMPC_CMD_RESDIS 0x1A 43 | 44 | ////////////////////////////////////////////////////////////////////////////// 45 | //Address mapping shorthand 46 | #define MAP_TO_SCSP(sh2map_snd_adr) ((sh2map_snd_adr - SNDRAM)) 47 | ////////////////////////////////////////////////////////////////////////////// 48 | //No Touchy Sound Ram Start! 49 | #define SNDRAM (631242752) 50 | // 1KB here is reserved for interrupts 51 | #define SNDPRG (SNDRAM + 0x408) 52 | //Also the end of sound RAM 53 | #define PCMEND (SNDRAM + 0x7F000) 54 | ////////////////////////////////////////////////////////////////////////////// 55 | #define DRV_SYS_END (47 * 1024) //System defined safe end of driver's address space 56 | #define PCM_CTRL_MAX (93) 57 | ////////////////////////////////////////////////////////////////////////////// 58 | #define PCM_ALT_LOOP (3) 59 | #define PCM_RVS_LOOP (2) 60 | #define PCM_FWD_LOOP (1) 61 | #define PCM_VOLATILE (0) 62 | #define PCM_PROTECTED (-1) 63 | #define PCM_SEMI (-2) 64 | #define ADX_STREAM (-3) 65 | ////////////////////////////////////////////////////////////////////////////// 66 | #define PCM_TYPE_ADX (2) // 4-bit (compressed audio) 67 | #define PCM_TYPE_8BIT (1) // 8-bit 68 | #define PCM_TYPE_16BIT (0) // 16-bit 69 | ////////////////////////////////////////////////////////////////////////////// 70 | #define PCM_SYS_REGION (0) //0 for NTSC, 1 for PAL 71 | ////////////////////////////////////////////////////////////////////////////// 72 | #define PCM_PAN_LEFT (1<<4) 73 | #define PCM_PAN_RIGHT (0) 74 | ////////////////////////////////////////////////////////////////////////////// 75 | #define ADX_MASTER_768 (0) 76 | #define ADX_MASTER_1152 (1) 77 | #define ADX_MASTER_1536 (2) 78 | #define ADX_MASTER_2304 (3) 79 | /* 7.68 Data */ 80 | #define ADX_768_COEF_1 (4401) 81 | #define ADX_768_COEF_2 (-1183) 82 | /* 11.52 data */ 83 | #define ADX_1152_COEF_1 (5386) 84 | #define ADX_1152_COEF_2 (-1771) 85 | /* 15.36 data */ 86 | #define ADX_1536_COEF_1 (5972) 87 | #define ADX_1536_COEF_2 (-2187) 88 | /* 23.04 data */ 89 | #define ADX_2304_COEF_1 (6631) 90 | #define ADX_2304_COEF_2 (-2685) 91 | ////////////////////////////////////////////////////////////////////////////// 92 | // 6400 Hz for PAL 93 | #define ADX_PAL_640 (4) 94 | #define ADX_640_COEF_1 (3915) 95 | #define ADX_640_COEF_2 (-936) 96 | // 9600 Hz for PAL 97 | #define ADX_PAL_960 (5) 98 | #define ADX_960_COEF_1 (4963) 99 | #define ADX_960_COEF_2 (-1504) 100 | // 12800 Hz for PAL 101 | #define ADX_PAL_1280 (6) 102 | #define ADX_1280_COEF_1 (5612) 103 | #define ADX_1280_COEF_2 (-1923) 104 | // 19200 Hz for PAL 105 | #define ADX_PAL_1920 (7) 106 | #define ADX_1920_COEF_1 (6359) 107 | #define ADX_1920_COEF_2 (-2469) 108 | 109 | 110 | typedef struct { 111 | char loopType; //[0,1,2,3] No loop, normal loop, reverse loop, alternating loop 112 | unsigned char bitDepth; //0, 1, or 2; 0 is 16-bit, 1 is 8-bit, 2 is ADX 113 | unsigned short hiAddrBits; //bits 19-16 of... 114 | unsigned short loAddrBits; //Two 16-bit chunks that when combined, form the start address of the sound. 115 | unsigned short LSA; //The # of samples forward from the start address to return to after loop. 116 | unsigned short playsize; //The # of samples to play before the sound shall loop. **Otherwise used as the length of the sound.** Do not leave at 0! 117 | //8 bit PCM is 1 byte per sample. 16 bit PCM is 2 bytes per sample. Therefore an 8bit PCM is a maximum of 64KB, and 16bit is 128KB. 118 | unsigned short pitchword; //the OCT & FNS word to use in the ICSR, verbatim. 119 | unsigned char pan; //Direct pan setting 120 | unsigned char volume; //Direct volume setting 121 | unsigned short bytes_per_blank; //Bytes the PCM will play every time the driver is run (vblank) 122 | unsigned short decompression_size; //Size of the buffer used for an ADX sound effect. Specifically sized by Master SH2. 123 | unsigned char sh2_permit; //Does the SH2 permit this command? If TRUE, run the command. If FALSE, key its ICSR OFF. 124 | char icsr_target; //Which explicit ICSR is this to land in? Can be controlled by SH2 or by driver. 125 | } _PCM_CTRL; //Driver Local Command Struct 126 | 127 | typedef struct { 128 | volatile unsigned int adx_stream_length; //Length of the ADX stream (in ADX frames) 129 | volatile unsigned short start; //System Start Boolean 130 | volatile char adx_buffer_pass[2]; //Booleans 131 | volatile short drv_adx_coef_1; //The (signed!) coefficient 1 the driver will use to build ADX multiplication tables. 132 | volatile short drv_adx_coef_2; //The (signed!) coefficient 2 the driver will use to build ADX multiplication tables. 133 | volatile _PCM_CTRL * pcmCtrl; 134 | volatile unsigned char cdda_left_channel_vol_pan; // Redbook left channel volume & pan. 135 | volatile unsigned char cdda_right_channel_vol_pan; // Redbook right channel volume & pan. 136 | } sysComPara; 137 | 138 | 139 | typedef struct { 140 | unsigned short one_half; //[this is 32768 or 0x8000] 141 | short offset2data; 142 | unsigned char format; //[this is 3] 143 | unsigned char block_size; //[this is 18] 144 | unsigned char bit_depth; //[this is 4] 145 | unsigned char channels; //[this should be 1] 146 | unsigned int sample_rate; 147 | unsigned int sample_ct; 148 | unsigned short hp_cutoff; //[this should be 500] 149 | unsigned char loop; 150 | unsigned char illegal; //[Boolean, 0 for false, 1 for true] 151 | } adx_header; 152 | 153 | // 154 | 155 | // 156 | extern sysComPara * m68k_com; 157 | extern unsigned int * scsp_load; 158 | extern unsigned short * master_volume; 159 | extern unsigned short driver_master_volume; 160 | extern short numberPCMs; 161 | 162 | // 163 | // System functions shared for pcmstm.c/h 164 | // 165 | short convert_bitrate_to_pitchword(short sampleRate); 166 | short calculate_bytes_per_blank(int sampleRate, Bool is8Bit, Bool isPAL); 167 | short lcm(short a, short b); 168 | void cd_init(void); 169 | 170 | // 171 | // These are likely to be duplicate commands from other libraries. 172 | // 173 | void smpc_wait_till_ready(void); 174 | void smpc_issue_command(unsigned char cmd); 175 | // 176 | // 177 | // 178 | 179 | short load_16bit_pcm(Sint8 * filename, int sampleRate); 180 | short load_8bit_pcm(Sint8 * filename, int sampleRate); 181 | short load_adx(Sint8 * filename); 182 | void load_drv(int master_adx_frequency); 183 | 184 | void set_master_volume(unsigned short volume); 185 | void pcm_play(short pcmNumber, char ctrlType, char volume); 186 | void pcm_parameter_change(short pcmNumber, char volume, char pan); 187 | void pcm_cease(short pcmNumber); 188 | 189 | // 190 | // Usage: 191 | // Intended as the "level reset" function. 192 | // Does not soft or hard reset driver. To do that, re-load the driver binary (run load_drv again). 193 | // This instead resets the loading pointer and number of PCMs to a specific PCM number. 194 | // In use with proper sequence of asset loading, a certain number of sound assets can be retained in sound memory, with others discarded. 195 | // 196 | // The argument "highest_pcm_number_to_keep" is the latest sequentially loaded PCM in sound RAM that signals the point at which: 197 | // Any PCM number loaded earlier than this will be kept in memory and its number still valid to play the sound. 198 | // Any PCM number loaded later than this will be ignored in memory when loading new sounds, but the number is still valid to play sound. 199 | void pcm_reset(short default_pcm_count); 200 | 201 | void sdrv_vblank_rq(void); 202 | 203 | // 204 | // Redbook support 205 | // Credit: ndiddy, ReyeMe, CyberWarriorX [Iapetus] 206 | // 207 | 208 | // ------------------------------------- 209 | // Types 210 | // ------------------------------------- 211 | 212 | /** @brief Track location data 213 | */ 214 | typedef struct 215 | { 216 | unsigned int Control:4; 217 | unsigned int Number:4; 218 | unsigned int fad:24; 219 | } CDTrackLocation; 220 | 221 | /** @brief Track information data 222 | */ 223 | typedef struct 224 | { 225 | unsigned char Control:4; 226 | unsigned char Address:4; 227 | unsigned char Number; 228 | union { 229 | short point; 230 | struct { 231 | char psec; 232 | char pframe; 233 | } pData; 234 | 235 | }pBody; 236 | 237 | } CDTrackInformation; 238 | 239 | /** @brief Session data 240 | */ 241 | typedef struct 242 | { 243 | unsigned int Control:4; 244 | unsigned int Address:4; 245 | unsigned int fad:24; 246 | } CDSession; 247 | 248 | /** @brief Table of contents 249 | */ 250 | #define MAX_CD_TRACK_COUNT (24) 251 | typedef struct 252 | { 253 | CDTrackLocation Tracks[MAX_CD_TRACK_COUNT]; 254 | CDTrackInformation FirstTrack; 255 | CDTrackInformation LastTrack; 256 | CDSession Session; 257 | } CDTableOfContents; 258 | 259 | 260 | void CDDA_SetVolume(int vol); 261 | void CDDA_SetChannelVolPan(unsigned char left_channel, unsigned char right_channel); 262 | void CDDA_Play(int fromTrack, int toTrack, Bool loop, int startAddress); 263 | void CDDA_PlaySingle(int track, Bool loop); 264 | int CDDA_Stop(void); 265 | 266 | 267 | #endif 268 | 269 | -------------------------------------------------------------------------------- /jo_demo/run_with_daemon_tools_and_ssf.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | 3 | SET EMULATOR_DIR=..\..\Emulators 4 | SET DT_DIR=C:\Program Files (x86)\DAEMON Tools Lite 5 | 6 | if exist game.iso ( 7 | echo Mounting image... 8 | "%DT_DIR%\DTLite.exe" -mount 0,game.cue 9 | cd "%EMULATOR_DIR%\SSF\" 10 | echo Running SSF... 11 | "SSF.exe" 12 | echo Unmounting image... 13 | "%DT_DIR%\DTLite.exe" -unmount 0 14 | ) else ( 15 | echo Please compile first ! 16 | pause 17 | ) 18 | -------------------------------------------------------------------------------- /jo_demo/run_with_mednafen.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET MEDNAFEN_EXECUTABLE_PATH=%EMULATOR_DIR%\mednafen\mednafen.exe 4 | 5 | if not exist %MEDNAFEN_EXECUTABLE_PATH% ( 6 | echo --- 7 | echo Please install Mednafen here %EMULATOR_DIR% 8 | echo --- 9 | pause 10 | exit 11 | ) 12 | 13 | if exist game.cue ( 14 | "%MEDNAFEN_EXECUTABLE_PATH%" "%cd%\game.cue" -sound.volume "150" 15 | ) else ( 16 | echo Please compile first ! 17 | ) 18 | -------------------------------------------------------------------------------- /jo_demo/run_with_nova.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET NOVA_BIOS_PATH=%EMULATOR_DIR%\nova\bios\bios.bin 4 | 5 | if not exist %NOVA_BIOS_PATH% ( 6 | echo --- 7 | echo Nova doesn't support HLE bios today. 8 | echo Therefore, please put Sega Saturn bios here %NOVA_BIOS_PATH% 9 | echo --- 10 | pause 11 | exit 12 | ) 13 | 14 | if exist game.iso ( 15 | "%EMULATOR_DIR%\nova\nova.exe" "%cd%\game.cue" 16 | ) else ( 17 | echo Please compile first ! 18 | ) 19 | -------------------------------------------------------------------------------- /jo_demo/run_with_powershell_and_ssf.ps1: -------------------------------------------------------------------------------- 1 | # /!\ THIS SCRIPT DOESN'T WORK TODAY BECAUSE OF SSF /!\ 2 | $isoPath = Join-Path $pwd.Path game.iso 3 | $ssfDirectory = Join-Path $pwd.Path "..\..\Emulators\SSF\" 4 | Write-Host "Mounting image..." 5 | Mount-DiskImage -StorageType ISO -ImagePath $isoPath 6 | Write-Host "Running SSF..." 7 | cd "$ssfDirectory" 8 | Start-Process -FilePath "SSF.exe" -Wait 9 | Write-Host "Unmounting image..." 10 | Get-DiskImage $isoPath | Dismount-DiskImage 11 | -------------------------------------------------------------------------------- /jo_demo/run_with_virtual_clone_drive_and_ssf.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET VCD_DIR=C:\Program Files (x86)\Elaborate Bytes\VirtualCloneDrive 4 | 5 | if exist game.iso ( 6 | echo Mounting image... 7 | "%VCD_DIR%\vcdmount.exe" game.iso 8 | cd "%EMULATOR_DIR%\SSF\" 9 | echo Running SSF... 10 | "SSF.exe" 11 | echo Unmounting image... 12 | "%VCD_DIR%\vcdmount.exe" /u 13 | ) else ( 14 | echo Please compile first ! 15 | pause 16 | ) 17 | -------------------------------------------------------------------------------- /jo_demo/run_with_yabaSanshiro.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | 4 | if exist game.iso ( 5 | echo Please wait, it's very slow, but I don't know why... 6 | "%EMULATOR_DIR%\YabaSanshiro\yabasanshiro.exe" -a -i game.iso 7 | ) else ( 8 | echo Please compile first ! 9 | ) 10 | -------------------------------------------------------------------------------- /jo_demo/run_with_yabause.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | 4 | if exist game.iso ( 5 | "%EMULATOR_DIR%\yabause\yabause.exe" -a -i game.iso 6 | ) else ( 7 | echo Please compile first ! 8 | ) 9 | -------------------------------------------------------------------------------- /jo_demo/run_with_yabause.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | command -v yabause >/dev/null 2>&1 || { echo "yabause is not installed.\ 3 | Aborting." >&2; exit 1; } 4 | 5 | if [ -f game.iso ]; 6 | then 7 | yabause -a -i game.cue 8 | else 9 | echo "Please compile first !" >&2 10 | fi 11 | -------------------------------------------------------------------------------- /jo_demo/use_mednafen.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | 3 | if exist game.iso ( 4 | "D:\games\med\mednafen.exe" game.cue 5 | ) else ( 6 | echo Please compile first ! 7 | ) -------------------------------------------------------------------------------- /jo_stream_demo/cd/ABS.TXT: -------------------------------------------------------------------------------- 1 | NOT Abstracted by SEGA 2 | 3 | -------------------------------------------------------------------------------- /jo_stream_demo/cd/BIB.TXT: -------------------------------------------------------------------------------- 1 | NOT Bibliographiced by SEGA 2 | 3 | -------------------------------------------------------------------------------- /jo_stream_demo/cd/CPY.TXT: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /jo_stream_demo/cd/animes.ADX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_stream_demo/cd/animes.ADX -------------------------------------------------------------------------------- /jo_stream_demo/cd/exert.PCM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_stream_demo/cd/exert.PCM -------------------------------------------------------------------------------- /jo_stream_demo/cd/gordon.ADX: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_stream_demo/cd/gordon.ADX -------------------------------------------------------------------------------- /jo_stream_demo/cd/mgear.PCM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_stream_demo/cd/mgear.PCM -------------------------------------------------------------------------------- /jo_stream_demo/cd/sdrv.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/jo_stream_demo/cd/sdrv.bin -------------------------------------------------------------------------------- /jo_stream_demo/clean.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET COMPILER_DIR=..\..\Compiler 3 | SET JO_ENGINE_SRC_DIR=../../jo_engine 4 | SET PATH=%COMPILER_DIR%\WINDOWS\Other Utilities;%PATH% 5 | 6 | rm -f ./cd/0.bin 7 | rm -f *.o 8 | rm -f %JO_ENGINE_SRC_DIR%/*.o 9 | rm -f ./*.bin 10 | rm -f ./*.coff 11 | rm -f ./*.elf 12 | rm -f ./*.map 13 | rm -f ./*.iso 14 | rm -f ./*.cue 15 | 16 | ECHO Done. 17 | -------------------------------------------------------------------------------- /jo_stream_demo/compile.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET COMPILER_DIR=..\..\Compiler 3 | SET PATH=%COMPILER_DIR%\WINDOWS\Other Utilities;%COMPILER_DIR%\WINDOWS\bin;%PATH% 4 | make re 5 | PAUSE -------------------------------------------------------------------------------- /jo_stream_demo/main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "pcmsys.h" 4 | #include "pcmstm.h" 5 | 6 | #define LWRAM (2097152) 7 | 8 | extern Sint8 SynchConst; //SGL System Variable 9 | int framerate; 10 | 11 | void * loading_system_scratch_buffer = (void*)LWRAM; 12 | 13 | void update_gamespeed(void) 14 | { 15 | int frmrt = delta_time>>6; 16 | jo_fixed_point_time(); 17 | 18 | static int lastTimes[66]; 19 | static int time_selector = 0; 20 | static int bad_frames = 0; 21 | 22 | lastTimes[time_selector] = frmrt; 23 | bad_frames += (frmrt > 40) ? 1 : 0; 24 | time_selector = (time_selector > 66) ? 0 : time_selector+1; 25 | 26 | framerate = (frmrt)>>4; 27 | jo_printf(0, 3, "(%i) Bad Frames)", bad_frames); 28 | 29 | if (framerate <= 0) framerate=1; 30 | else if (framerate > 5) framerate=5; 31 | 32 | //Framegraph 33 | char curLine = frmrt; 34 | char prevLine = (time_selector < 1) ? lastTimes[65] : lastTimes[time_selector-1]; 35 | char nthLine = (time_selector < 2) ? lastTimes[65] : lastTimes[time_selector-2]; 36 | 37 | jo_draw_background_line(time_selector+4, 22, time_selector+4, 8, 0xC210); 38 | jo_draw_background_line(time_selector+4, 22, time_selector+4, (curLine>>2)+6, 0x8200); 39 | if(time_selector > 1){ 40 | jo_draw_background_line((time_selector-1)+4, 22, (time_selector-1)+4, (prevLine>>2)+6, 0xC000); 41 | } 42 | if(time_selector > 2){ 43 | jo_draw_background_line((time_selector-2)+4, 22, (time_selector-2)+4, (nthLine>>2)+6, 0x8010); 44 | } 45 | // 46 | 47 | if(jo_is_pad1_key_pressed(JO_KEY_START | JO_KEY_A | JO_KEY_B | JO_KEY_C)) 48 | { 49 | SYS_Exit(0); 50 | } 51 | } 52 | 53 | ////////////////////////////////////////////////////////////////////////////// 54 | //Sound Numbers 55 | ////////////////////////////////////////////////////////////////////////////// 56 | int snd_adx = 0; 57 | int snd_exert = 0; 58 | ////////////////////////////////////////////////////////////////////////////// 59 | 60 | void my_load(void) 61 | { 62 | 63 | snd_adx = add_adx_front_buffer(23040); 64 | add_adx_back_buffer((void*)LWRAM); 65 | 66 | snd_exert = load_8bit_pcm((Sint8*)"EXERT.PCM", 11520); 67 | 68 | } 69 | 70 | void my_draw(void) 71 | { 72 | update_gamespeed(); 73 | 74 | jo_printf(1, 5, "Welcome to the streaming sound demo."); 75 | jo_printf(1, 6, "Press A to hear about Gordon. (ADX)"); 76 | jo_printf(1, 7, "Press Z to hear about animes. (ADX)"); 77 | jo_printf(1, 8, "These are ADX streams. Only one at a time."); 78 | jo_printf(1, 9, "The ADX streams can be as long as you like."); 79 | jo_printf(1, 10, "Notice how quickly they start playing!"); 80 | jo_printf(1, 11, "Press B to stop an ADX stream."); 81 | 82 | jo_printf(1, 13, "Press X to start playing music."); 83 | jo_printf(1, 14, "The music is a raw PCM stream."); 84 | jo_printf(1, 15, "Notice how long it takes to buffer?"); 85 | jo_printf(1, 16, "Press Y to stop the PCM stream."); 86 | jo_printf(1, 17, "The PCM stream will loop a set # of times."); 87 | 88 | jo_printf(1, 19, "ADX and PCM can stream simultaneously."); 89 | 90 | jo_printf(1, 21, "Lastly, you can press C to play a PCM."); 91 | jo_printf(1, 22, "Have fun with the source code ..."); 92 | jo_printf(1, 23, "It's a mess! I love it!"); 93 | 94 | if(jo_is_input_key_pressed(0, JO_KEY_A)) 95 | { 96 | start_adx_stream((Sint8*)"GORDON.ADX", 6); 97 | } 98 | 99 | if(jo_is_input_key_pressed(0, JO_KEY_Z)) 100 | { 101 | start_adx_stream((Sint8*)"ANIMES.ADX", 6); 102 | } 103 | 104 | if(jo_is_input_key_pressed(0, JO_KEY_B)) 105 | { 106 | stop_adx_stream(); 107 | } 108 | 109 | if(jo_is_input_key_pressed(0, JO_KEY_X)) 110 | { 111 | start_pcm_stream((Sint8*)"MGEAR.PCM", 5); 112 | stm.times_to_loop = 1; 113 | } 114 | 115 | if(jo_is_input_key_pressed(0, JO_KEY_Y)) 116 | { 117 | stop_pcm_stream(); 118 | stm.times_to_loop = 0; 119 | } 120 | 121 | if(jo_is_input_key_pressed(0, JO_KEY_C)) 122 | { 123 | pcm_play(snd_exert, PCM_SEMI, 7); 124 | } 125 | 126 | } 127 | 128 | void jo_main(void) 129 | { 130 | //////////////////////////////////////////////// 131 | // REMINDER: All file names must comply with the 8.3 standard. 132 | // File extensions can be no longer than 3 letters. 133 | // File names can be no longer than 8 letters. 134 | // The total length is thusly 12 characters (as there is a period). 135 | //////////////////////////////////////////////// 136 | jo_core_init(JO_COLOR_Black); 137 | SynchConst=2; 138 | load_drv(ADX_MASTER_2304); 139 | my_load(); 140 | 141 | jo_core_add_vblank_callback(sdrv_stm_vblank_rq); 142 | jo_core_add_vblank_callback(sdrv_vblank_rq); 143 | pcm_stream_init(30720, PCM_TYPE_8BIT); 144 | pcm_stream_host(my_draw); 145 | } 146 | 147 | /* 148 | ** END OF FILE 149 | */ 150 | 151 | -------------------------------------------------------------------------------- /jo_stream_demo/makefile: -------------------------------------------------------------------------------- 1 | JO_COMPILE_WITH_VIDEO_MODULE = 0 2 | JO_COMPILE_WITH_BACKUP_MODULE = 0 3 | JO_COMPILE_WITH_TGA_MODULE = 0 4 | JO_COMPILE_WITH_AUDIO_MODULE = 0 5 | JO_COMPILE_WITH_3D_MODULE = 0 6 | JO_COMPILE_WITH_PSEUDO_MODE7_MODULE = 0 7 | JO_COMPILE_WITH_EFFECTS_MODULE = 0 8 | JO_PSEUDO_SATURN_KAI_SUPPORT = 1 9 | JO_COMPILE_WITH_DUAL_CPU_MODULE = 0 10 | JO_DEBUG = 0 11 | JO_COMPILE_USING_SGL=1 12 | JO_NTSC = 1 13 | SRCS=main.c pcmsys.c pcmstm.c 14 | JO_ENGINE_SRC_DIR=../../jo_engine 15 | COMPILER_DIR=../../Compiler 16 | include $(COMPILER_DIR)/COMMON/jo_engine_makefile 17 | -------------------------------------------------------------------------------- /jo_stream_demo/pcmstm.c: -------------------------------------------------------------------------------- 1 | // 2 | // pcmstm.c 3 | // Note: this file is compiled separately 4 | // 5 | #include 6 | #include 7 | #include "pcmsys.h" 8 | #include "pcmstm.h" 9 | #define true (1) 10 | #define false (0) 11 | 12 | _pcm_stm_param stm; 13 | _pcm_stm_buf_ctrl buf; 14 | _generic_file_ctrl file; 15 | 16 | int file_transfer_sector = 9; 17 | int file_transfer_size = (9 * 2048); 18 | int file_system_status_reporting = REPORT_IDLE; 19 | 20 | adx_stream_param adx_stream; 21 | 22 | _file_request_entry file_request_list[MAX_FILE_REQUESTS]; 23 | _file_request_entry * active_request; 24 | int number_of_requests = 0; 25 | 26 | void start_file_stream(Sint32 id, void * destination) 27 | { 28 | if(adx_stream.file.transfer_lock || file.requested || file.setup_requested) return; 29 | file.id = id; 30 | file.setup_requested = true; 31 | file.destination = destination; 32 | } 33 | 34 | void file_request_manager(void) 35 | { 36 | // Don't process if file access is already happening. 37 | if(file.requested || file.setup_requested || adx_stream.file.transfer_lock) return; 38 | for(int i = number_of_requests; i > -1; i--) 39 | { 40 | if(file_request_list[i].active && !file_request_list[i].done) 41 | { 42 | start_file_stream(file_request_list[i].id, file_request_list[i].destination); 43 | active_request = &file_request_list[i]; 44 | break; 45 | } 46 | } 47 | } 48 | 49 | void new_file_request(Sint8 * filename, void * destination, void (*handler_function)(void *), short immediate_or_async) 50 | { 51 | if(number_of_requests >= MAX_FILE_REQUESTS) return; 52 | file_request_list[number_of_requests].id = GFS_NameToId(filename); 53 | file_request_list[number_of_requests].destination = destination; 54 | file_request_list[number_of_requests].handler_function = handler_function; 55 | file_request_list[number_of_requests].active = 1; 56 | file_request_list[number_of_requests].done = 0; 57 | file_request_list[number_of_requests].immediate_or_async = immediate_or_async; 58 | number_of_requests++; 59 | } 60 | 61 | void new_special_request(Sint8 * filename, void * destination, void (*handler_function)(Sint32, void *)) 62 | { 63 | if(number_of_requests >= MAX_FILE_REQUESTS) return; 64 | file_request_list[number_of_requests].id = GFS_NameToId(filename); 65 | file_request_list[number_of_requests].destination = destination; 66 | file_request_list[number_of_requests].special_handler_function = handler_function; 67 | file_request_list[number_of_requests].active = 1; 68 | file_request_list[number_of_requests].done = 0; 69 | file_request_list[number_of_requests].immediate_or_async = HANDLE_SPECIAL; 70 | number_of_requests++; 71 | } 72 | 73 | 74 | void load_file_list_immediate(void) 75 | { 76 | //To mute any... strangeness. 77 | *master_volume = 0x200; 78 | 79 | GfsHn gfs_ea; 80 | Sint32 file_size; 81 | 82 | for(int i = number_of_requests; i > -1; i--) 83 | { 84 | if(file_request_list[i].active && !file_request_list[i].done) 85 | { 86 | if(file_request_list[i].immediate_or_async != HANDLE_SPECIAL) 87 | { 88 | //Open GFS 89 | gfs_ea = GFS_Open(file_request_list[i].id); 90 | //Get size 91 | GFS_GetFileInfo(gfs_ea, NULL, NULL, &file_size, NULL); 92 | //Close it again 93 | GFS_Close(gfs_ea); 94 | 95 | GFS_Load(file_request_list[i].id, 0, (Uint32 *)file_request_list[i].destination, file_size); 96 | 97 | file_request_list[i].handler_function(file_request_list[i].destination); 98 | } else { 99 | //Special type handling 100 | file_request_list[i].special_handler_function(file_request_list[i].id, file_request_list[i].destination); 101 | } 102 | 103 | // if(file_request_list[i].immediate_or_async == HANDLE_SPECIAL) 104 | // { 105 | 106 | // file_request_list[i].special_handler_function(file_request_list[i].id, file_request_list[i].destination); 107 | 108 | // } 109 | 110 | file_request_list[i].active = 0; 111 | file_request_list[i].done = 1; 112 | number_of_requests--; 113 | 114 | } 115 | } 116 | 117 | set_master_volume(driver_master_volume); 118 | } 119 | 120 | void finish_file_request(void) 121 | { 122 | active_request->handler_function(active_request->destination); 123 | active_request->active = 0; 124 | active_request->done = 1; 125 | number_of_requests--; 126 | } 127 | 128 | /* 129 | 130 | Will set the process-in-motion to open the (filename) and play it back as a PCM stream. 131 | The parameters of the PCM stream are to be pre-set by the function "pcm_stream_init". 132 | There is no check to see if it's set-up yet. Ah well, I'm sure you could figure it out. 133 | 134 | Note, unless the game code is operating within the "pcm_stream_host" function, this will do nothing. 135 | 136 | */ 137 | void start_pcm_stream(Sint8 * filename, int volume) 138 | { 139 | //If a stream is already playing, re-start it with the new file-name. 140 | if(buf.operating || buf.setup_requested) 141 | { 142 | stm.stopping = true; 143 | stm.restarting = true; 144 | } 145 | buf.file_id = GFS_NameToId(filename); 146 | buf.setup_requested = true; 147 | stm.volume = volume; 148 | } 149 | 150 | //Note: You really can't change the pitch of these; messes with it too much. 151 | void change_pcm_stream_param(char volume, char pan) 152 | { 153 | pcm_parameter_change(stm.pcm_num, volume, pan); 154 | } 155 | void change_adx_stream_param(char volume, char pan) 156 | { 157 | pcm_parameter_change(adx_stream.pcm_number, volume, pan); 158 | } 159 | 160 | // Will stop a PCM stream. 161 | void stop_pcm_stream(void) 162 | { 163 | 164 | stm.stopping = true; 165 | 166 | } 167 | 168 | // Will stop an ADX stream. 169 | // Note the driver needn't use this function to stop an ADX stream in case of it finishing the file. 170 | // In that case, the driver will know when to properly stop playback. 171 | // This function is for premature stoppage of the sound. 172 | void stop_adx_stream(void) 173 | { 174 | // if(adx_stream.active) 175 | // { 176 | m68k_com->pcmCtrl[adx_stream.pcm_number].sh2_permit = 0; 177 | adx_stream.request_stop = 1; 178 | // } 179 | } 180 | 181 | // 182 | /* 183 | Add raw PCM buffer 184 | This function adds a PCM slot with parameters with a given size in sound RAM. 185 | It does so without loading any data to that region of sound RAM. 186 | The sound RAM pointer is properly incremented such that this region of memory, and this sound slot, are a playable buffer. 187 | */ 188 | // 189 | short add_raw_pcm_buffer(Bool is8Bit, short sampleRate, int size) 190 | { 191 | 192 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 193 | 194 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)scsp_load >> 16); 195 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)scsp_load & 0xFFFF); 196 | 197 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(sampleRate); 198 | m68k_com->pcmCtrl[numberPCMs].playsize = (is8Bit) ? size : size>>1; 199 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = calculate_bytes_per_blank(sampleRate, is8Bit, PCM_SYS_REGION); //Iniitalize as max volume 200 | m68k_com->pcmCtrl[numberPCMs].bitDepth = (is8Bit) ? PCM_TYPE_8BIT : PCM_TYPE_16BIT; 201 | m68k_com->pcmCtrl[numberPCMs].loopType = 0; //Initialize as non-looping 202 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Iniitalize as max volume 203 | numberPCMs++; 204 | scsp_load = (unsigned int *)((unsigned int )scsp_load + size); 205 | return (numberPCMs-1); //Return the PCM # this sound received 206 | } 207 | 208 | /* 209 | This function adds an ADX entry to the PCM CTRL list and allocates an 18KB front-buffer in sound RAM to play it back from. 210 | This is used for ADX streams only. (Normal ADX sound effects do not use or need this) 211 | The only allowed bit-rates are 7680, 11520, 15360, and 23040. 212 | These are the specific bit-rates allowed due to the fact they coincide with the uncompressed byte-rates of: 213 | 256, 384, 512, and 768. These are exact byte-rates, which is important. 214 | */ 215 | short add_adx_front_buffer(short bit_rate) 216 | { 217 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 218 | 219 | int adx_buf_sz = (18 * 1024); 220 | /** 221 | NOTICE: 222 | When playing this back, you will need to offset the start by 2 frames. 223 | The driver should know to do that on its own when playing an ADX_STREAM. 224 | That will get over the header and offset to data (total 36 bytes). 225 | **/ 226 | volatile int i = 0; 227 | for(i = 0; i < (int)scsp_load; i++) 228 | { 229 | //This is to pop the stack here. Because GCC. 230 | } 231 | adx_stream.front_buffer[0] = (void *)((unsigned int)scsp_load + SNDRAM); 232 | adx_stream.front_buffer[1] = (void *)((unsigned int)scsp_load + SNDRAM + (adx_buf_sz>>1)); 233 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned int)(scsp_load)>>16; 234 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned int)(scsp_load) & 0xFFFF; 235 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(bit_rate); 236 | m68k_com->pcmCtrl[numberPCMs].playsize = 18384; //TBD by stream system, so just set it at a high number. 237 | short bpb = calculate_bytes_per_blank((int)bit_rate, false, PCM_SYS_REGION); //Iniitalize as max volume 238 | if(bpb != 768 && bpb != 512 && bpb != 384 && bpb != 256 && bpb != 192 && bpb != 128) 239 | { 240 | slPrint("!(ADX INVALID BYTE-RATE)!", slLocate(0, 1)); 241 | return -2; 242 | } 243 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = bpb; 244 | m68k_com->pcmCtrl[numberPCMs].decompression_size = (bpb >= 256) ? lcm(bpb, bpb + 64)<<1 : 5376; // Dirty fix for ultra low bitrate 245 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_ADX; //Select ADX type 246 | m68k_com->pcmCtrl[numberPCMs].loopType = ADX_STREAM; //Initialize as ADX stream. 247 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Iniitalize as max volume 248 | 249 | numberPCMs++; 250 | scsp_load = (unsigned int *)((unsigned int )scsp_load + adx_buf_sz); 251 | adx_stream.front_buffer_okay = true; 252 | adx_stream.pcm_number = numberPCMs-1; 253 | return (numberPCMs-1); //Return the PCM # this sound received 254 | } 255 | 256 | 257 | /* 258 | Allocate the ADX back-buffer. It can be anywhere in HWRAM or LWRAM. 259 | Technically plausible to put it in sound RAM, but this isn't recommended. 260 | This is used only for ADX streams. (Normal ADX sound effects do not use or need this) 261 | */ 262 | void add_adx_back_buffer(void * t18kb_buffer_location) 263 | { 264 | adx_stream.back_buffer[0] = t18kb_buffer_location; 265 | adx_stream.back_buffer[1] = t18kb_buffer_location + (9 * 1024); 266 | adx_stream.back_buffer_okay = true; 267 | } 268 | 269 | /* 270 | 271 | This function performs the DMA transfers from the ADX back buffer to the front buffer. 272 | Note that it does not schedule them; the M68k software schedules the transfers. 273 | 274 | */ 275 | void operate_adx_stream(void) 276 | { 277 | //This condition halts operation of the ADX Stream if the front/back buffer are not set up yet. 278 | if(!adx_stream.front_buffer_okay || !adx_stream.back_buffer_okay) return; 279 | if(adx_stream.active && !adx_stream.playing) 280 | { 281 | //This condition will read the header of the file and set it to play. 282 | //read_adx_at_front_buffer(stm); 283 | adx_stream.playing = true; 284 | } else if(adx_stream.active && adx_stream.playing) 285 | { 286 | //This condition will begin managing copying the back to the front buffer. 287 | //This management depends on the sound driver writing "1" to the adx_buffer_pass variable when it passes points in the buffer. 288 | //It only writes to them. The driver itself then depends on this software to exchange the front and back buffers as needed. 289 | if(m68k_com->adx_buffer_pass[0]) 290 | { 291 | //In this condition, we are to copy back buffer [zero] to front buffer [zero]. 292 | //This is 9KB of data (half of 18KB buffer). 293 | slDMACopy(adx_stream.back_buffer[0], adx_stream.front_buffer[0], (1024 * 9)); 294 | m68k_com->adx_buffer_pass[0] = 0; 295 | adx_stream.back_buffer_filled[0] = false; 296 | } 297 | if(m68k_com->adx_buffer_pass[1]) 298 | { 299 | //In this condition, we are to copy back buffer [one] to front buffer [one]. 300 | //This is 9KB of data (half of 18KB buffer). 301 | slDMACopy(adx_stream.back_buffer[1], adx_stream.front_buffer[1], (1024 * 9)); 302 | m68k_com->adx_buffer_pass[1] = 0; 303 | adx_stream.back_buffer_filled[1] = false; 304 | } 305 | } 306 | } 307 | 308 | /* 309 | 310 | Will set the process in motion to start an ADX stream from (filename) with (volume). 311 | The ADX stream will be played back according to the parameters set by the front buffer (bitrate, mostly). 312 | Note that if your game code is not operating from with the "pcm_stream_host" function, this will do nothing. 313 | 314 | */ 315 | void start_adx_stream(Sint8 * filename, short volume) 316 | { 317 | // This condition halts operation of the ADX Stream if the front/back buffer are not set up yet. 318 | if(!adx_stream.front_buffer_okay || !adx_stream.back_buffer_okay) return; 319 | // This condition... prevents bugs. 320 | if(adx_stream.file.requested || adx_stream.file.setup_requested || adx_stream.active) return; 321 | 322 | adx_stream.file.id = GFS_NameToId(filename); 323 | //nbg_sprintf(0, 17, "started ADX stm"); 324 | adx_stream.active = true; 325 | adx_stream.file.setup_requested = true; 326 | adx_stream.volume = volume; 327 | //idk this is stupid shouldn't have to do it but for now i do 328 | //Why: 329 | //Before data is copied over, a valid ADX header is copied to this location. 330 | //When there's already a valid header here, the driver will decompress it into the playback buffer. 331 | //That's a problem if it's old data, from the wrong ADX stream. 332 | //So we have to purge it. We shouldn't have to; the data is copied here before the stream is commanded to play... but we do. 333 | unsigned short * writedummy = (unsigned short *)adx_stream.back_buffer[0]; 334 | for(int i = 0; i < (m68k_com->pcmCtrl[adx_stream.pcm_number].bytes_per_blank<<2); i++) 335 | { 336 | *writedummy++ = 0; 337 | } 338 | } 339 | 340 | 341 | /* 342 | 343 | This function initializes the PCM stream buffer according to the parameters of the bit-rate and depth of the PCM files used. 344 | It's recommended to use 30720 KHz 8-bit PCMs. 345 | It is essential to use this function before the program enters pcm_stream_host. 346 | It is also helpful to use this function before any other sound effects are loaded. 347 | These parameters, currently, may only be set once. I haven't established the process required to change them mid-program. 348 | 349 | */ 350 | void pcm_stream_init(int bitrate, int bit_depth) 351 | { 352 | 353 | static Bool only_run_once = false; 354 | 355 | if(only_run_once == false) 356 | { 357 | 358 | short byte_rate = calculate_bytes_per_blank(bitrate, bit_depth, PCM_SYS_REGION); 359 | 360 | buf.buffer_size_bytes = byte_rate * PCM_BUFFERED_BLANKS; //"byte_rate" being the bytes per blank of the music track 361 | buf.segment_size = buf.buffer_size_bytes / NUM_PCM_BUF; 362 | buf.transfer_sectors = buf.segment_size / 2048; 363 | buf.transfer_bytes = (buf.transfer_sectors * 2048); 364 | buf.transfer_timing = buf.buffer_size_bytes / buf.transfer_bytes; 365 | 366 | buf.segment_transfer_time = buf.segment_size / buf.transfer_bytes; 367 | 368 | buf.buffer_location_in_sndram = (void *)((unsigned int)scsp_load + SNDRAM); 369 | for(short i = 0; i < NUM_PCM_BUF; i++) 370 | { 371 | buf.segment_locations[i] = (void *)((unsigned int)buf.buffer_location_in_sndram + (buf.segment_size * i)); 372 | buf.segment_refresh_timings[i] = (PCM_BUFFERED_BLANKS / NUM_PCM_BUF) * (i+1); 373 | } 374 | stm.pcm_num = add_raw_pcm_buffer(bit_depth, bitrate, buf.buffer_size_bytes); 375 | only_run_once = true; 376 | // I almost forgot to post-increment the loading pointer by the size of the buffer 377 | scsp_load += buf.buffer_size_bytes; 378 | } 379 | } 380 | 381 | ///////////////////// 382 | /* 383 | 384 | Important Notice 385 | 386 | The following is a *unique* function for operating the driver and PCM/ADX streams. 387 | Do not run this function alongside the normal "sdrv_vblank_rq". 388 | Run this function instead of that one only in case where the project will use PCM and/or ADX streams. 389 | 390 | */ 391 | ///////////////////// 392 | void sdrv_stm_vblank_rq(void) 393 | { 394 | if(buf.operating) 395 | { 396 | /////////////////////////// 397 | // TIME COUNTERS (VBLANKS) 398 | if(stm.playing) stm.played_blanks++; 399 | buf.vblank_counter++; 400 | 401 | for(short b = 0; b < NUM_PCM_BUF; b++) 402 | { 403 | if(buf.vblank_counter == buf.segment_refresh_timings[b]) 404 | { 405 | /////////////////////////////////// 406 | // BUFFER CONSUMPTION CONTROL 407 | buf.active_buf_segment = b; 408 | buf.segment_full[b] = false; 409 | buf.needs_buffer_filled = true; 410 | buf.steps_of_new_data_in_buffer = 0; 411 | } 412 | } 413 | 414 | if(buf.vblank_counter >= PCM_BUFFERED_BLANKS) 415 | { 416 | buf.steps_of_new_data_in_buffer = 0; 417 | buf.vblank_counter = 0; 418 | //////////////////////////////////// 419 | // SOUND STARTING 420 | // This is the appropriate place to start playing the sound if this loading period was the first one. 421 | // This ensures we don't play old/garbage data in the buffer: playing starts always after new loading is done. 422 | // The control data is flagged here so we don't needlessly re-send the command (though, it wouldn't hurt if we do). 423 | // Thought: If you wanted to change the pitch word / effective bit-rate of the music, this is also where you'd do that. 424 | // But that's a lot of math (like changing the vblank timer) to do. 425 | if(!stm.playing) 426 | { 427 | stm.playing = true; 428 | pcm_play(stm.pcm_num, PCM_FWD_LOOP, stm.volume); 429 | } 430 | } 431 | ///////////////////////////////////////////// 432 | // STREAM ENDING / LOOPING 433 | // This is the appropriate place & time to set up looping or ending for a PCM stream. 434 | // I admit, it is slightly off. The mathemagics suggest that the timing must be shifted forward. 435 | // The forward-time-shift of the loop or ending is caused by the delay in playing the sound from when the sound was loaded. 436 | // Or another effect, that I documented in the driver binary, but that's not important. At least, THAT important. 437 | // When a stream is set to loop, note that it will close and re-open the file. It will not stop the sound, though. 438 | // 439 | if(stm.played_blanks > (stm.total_blanks - (PCM_BUFFERED_BLANKS))) 440 | { 441 | if(stm.times_to_loop <= 0) 442 | { 443 | stm.stopping = true; 444 | } else { 445 | stm.stopping = true; 446 | stm.times_to_loop--; 447 | stm.restarting = true; 448 | stm.played_blanks = 0; 449 | } 450 | } 451 | 452 | //nbg_sprintf(2, 7, "blanks(%i)", buf.vblank_counter); 453 | } 454 | 455 | /////////////////////////////// 456 | // ADX STREAM OPERATOR 457 | // Side-note: Because of how ADX works, streaming ADX is oddly more well-developed. The M68K handles a lot of the timing. 458 | if(adx_stream.active) 459 | { 460 | operate_adx_stream(); 461 | } 462 | } 463 | 464 | void pcm_stream_host(void(*game_code)(void)) 465 | { 466 | 467 | static Sint32 gfs_svr_status; 468 | static Sint32 bytes_read_now; 469 | static Sint32 byte_dummy; 470 | 471 | RESTART: 472 | 473 | cd_init(); 474 | 475 | while(1) 476 | { 477 | //Main system loop - system doesn't break away from this. 478 | /* 479 | Hi. This is a mess. Spaghetti, if you will. Don't you just love it? 480 | Branches: 481 | --> PCM needs setup... 482 | | File system set-up 483 | --> PCM should stop... 484 | | Clean stream parameters 485 | | | -> Restarting, stop sound, queue setup again 486 | | | -> Not restarting, stop sound 487 | | Close file 488 | --> PCM Inactive... 489 | | | -> No activity 490 | | | -> File system CD -> RAM 491 | | | -> File system completion 492 | | | -> File system set-up 493 | | | -> ADX, CD -> RAM, 18KB 494 | | | -> ADX, Completion / closure 495 | | | -> ADX, set-up 496 | --> PCM Active... 497 | | -> Operate CD -> RAM Activity, one-buffer segment 498 | 499 | */ 500 | 501 | if(buf.setup_requested) 502 | { 503 | ///////////// 504 | //slPrint("--SETM--", slLocate(16,2)); 505 | file_system_status_reporting = REPORT_SETTING_UP_PCMSTM; 506 | //game_code(); 507 | buf.file_handle = GFS_Open(buf.file_id); 508 | /* 509 | In testing, it has been found that these lines are _strictly necessary_ for this to work. 510 | SetReadPara sets the bytes per request to be retrieved from CD to the CD block buffer. 511 | SetTransPara sets the sectors per request to be transferred from the CD block buffer to system addressable memory. 512 | SetTmode sets the DMA channel to be used for the request. We set SDMA1 specifically. SDMA0 can be used. 513 | */ 514 | GFS_SetReadPara(buf.file_handle, buf.buffer_size_bytes); 515 | GFS_SetTransPara(buf.file_handle, buf.transfer_sectors); 516 | GFS_SetTmode(buf.file_handle, GFS_TMODE_SDMA0); 517 | 518 | GFS_GetFileInfo(buf.file_handle, NULL, NULL, &buf.total_bytes, NULL); 519 | stm.total_blanks = buf.total_bytes / (int)m68k_com->pcmCtrl[stm.pcm_num].bytes_per_blank; 520 | stm.played_blanks = 0; 521 | 522 | for(short b = 0; b < NUM_PCM_BUF; b++) 523 | { 524 | buf.segment_full[b] = false; 525 | } 526 | buf.vblank_counter = 0; 527 | buf.operating = true; 528 | 529 | buf.setup_requested = false; 530 | ////////////// 531 | //slSynch(); 532 | } else if(stm.stopping) 533 | { 534 | //game_code(); 535 | buf.operating = false; 536 | buf.needs_buffer_filled = false; 537 | stm.stopping = false; 538 | if(stm.restarting) 539 | { 540 | buf.setup_requested = true; 541 | stm.restarting = false; 542 | stm.playing = false; 543 | pcm_cease(stm.pcm_num); 544 | } else { 545 | stm.playing = false; 546 | pcm_cease(stm.pcm_num); 547 | } 548 | GFS_Close(buf.file_handle); 549 | 550 | 551 | //stop_adx_stream(); 552 | //adx_stream.file.requested = false; 553 | //adx_stream.file.transfer_lock = false; 554 | //adx_stream.file.setup_requested = false; 555 | //adx_stream.active = false; 556 | //adx_stream.playing = false; 557 | //adx_stream.request_stop = false; 558 | //GFS_Close(adx_stream.file.handle); 559 | game_code(); 560 | slSynch(); 561 | // goto RESTART; //(Possible) ugly crash fix? 562 | //slSynch(); 563 | } else if(!buf.needs_buffer_filled) 564 | { 565 | //Broadly speaking, this branch is generic file system or ADX streaming in the PCM stream's free time. 566 | if((file.transfer_lock || !file.requested) && !file.setup_requested && !adx_stream.file.setup_requested && !adx_stream.request_stop 567 | && (!adx_stream.file.requested || (adx_stream.back_buffer_filled[0] || adx_stream.back_buffer_filled[1])) ) 568 | { 569 | //This branch is for no file system activity. Nothing is being presently accessed or requested to be set up. 570 | file_system_status_reporting = REPORT_IDLE; 571 | //slPrint("--PLAY--", slLocate(16,2)); 572 | // nbg_sprintf(16, 6, "bufrq(%i)", buf.needs_buffer_filled); 573 | // nbg_sprintf(2, 8, "buf0f(%i)", buf.segment_full[0]); 574 | // nbg_sprintf(2, 9, "buf1f(%i)", buf.segment_full[1]); 575 | // nbg_sprintf(16, 8, "filerq(%i)", file.requested); 576 | // nbg_sprintf(16, 9, "ftrans(%i)", file.transfer_lock); 577 | // nbg_sprintf(2, 10, "fsetup(%i)", file.setup_requested); 578 | // nbg_sprintf(16, 10, "fsect(%i)", file.sectors_read_so_far); 579 | // nbg_sprintf(2, 12, "fsize(%i)", file.total_sectors); 580 | // nbg_sprintf(16, 12, "fnsct(%i)", file.total_bytes); 581 | // nbg_sprintf(2, 6, "bytes(%i)", bytes_read_now); 582 | 583 | // nbg_sprintf(2, 5, "steps(%i)", buf.vblank_counter); 584 | // nbg_sprintf(2, 6, "segn(%i)", buf.active_buf_segment); 585 | // nbg_sprintf(2, 8, "buf0f(%i)", buf.segment_full[0]); 586 | // nbg_sprintf(2, 9, "buf1f(%i)", buf.segment_full[1]); 587 | // nbg_sprintf(2, 10, "buf2f(%i)", buf.segment_full[2]); 588 | // nbg_sprintf(16, 8, "buf0t(%i)", buf.segment_refresh_timings[0]); 589 | // nbg_sprintf(16, 9, "buf1t(%i)", buf.segment_refresh_timings[1]); 590 | // nbg_sprintf(16, 10, "buf2t(%i)", buf.segment_refresh_timings[2]); 591 | // nbg_sprintf(16, 6, "stpl(%i)", stm.playing); 592 | 593 | // nbg_sprintf(2, 8, "adx0f(%i)", adx_stream.back_buffer_filled[0]); 594 | // nbg_sprintf(2, 9, "adx1f(%i)", adx_stream.back_buffer_filled[1]); 595 | // nbg_sprintf(16, 8, "adxrq(%i)", adx_stream.file.requested); 596 | // nbg_sprintf(16, 9, "a_acti(%i)", adx_stream.active); 597 | // nbg_sprintf(2, 10, "asetup(%i)", adx_stream.file.setup_requested); 598 | // nbg_sprintf(16, 10, "asect(%i)", adx_stream.file.sectors_read_so_far); 599 | // nbg_sprintf(2, 12, "asize(%i)", adx_stream.file.total_bytes); 600 | // nbg_sprintf(16, 12, "ansct(%i)", adx_stream.file.total_sectors); 601 | ///////////// 602 | file_request_manager(); 603 | if(!buf.operating) 604 | { 605 | //Un-lock file system in case of PCM stream being paused. 606 | file.transfer_lock = false; 607 | } 608 | ///////////// 609 | game_code(); 610 | slSynch(); 611 | } else if(file.requested && file.sectors_read_so_far < file.total_sectors) 612 | { 613 | //This branch is for serving an active file request from CD to RAM. 614 | GFS_NwFread(file.handle, file_transfer_sector, 615 | file.destination + (file.sectors_read_so_far * 2048), file_transfer_size); 616 | do{ 617 | file_system_status_reporting = REPORT_LOADING_FILE; 618 | //slPrint("--FILE--", slLocate(16,2)); 619 | // nbg_sprintf(2, 6, "bytes(%i)", byte_dummy); 620 | // nbg_sprintf(16, 6, "bufrq(%i)", buf.needs_buffer_filled); 621 | // nbg_sprintf(2, 8, "buf0f(%i)", buf.segment_full[0]); 622 | // nbg_sprintf(2, 9, "buf1f(%i)", buf.segment_full[1]); 623 | // nbg_sprintf(16, 8, "filerq(%i)", file.requested); 624 | // nbg_sprintf(16, 9, "ftrans(%i)", file.transfer_lock); 625 | // nbg_sprintf(2, 10, "fsetup(%i)", file.setup_requested); 626 | // nbg_sprintf(16, 10, "fsect(%i)", file.sectors_read_so_far); 627 | // nbg_sprintf(2, 12, "fsize(%i)", file.total_sectors); 628 | // nbg_sprintf(16, 12, "fnsct(%i)", file.total_bytes); 629 | //////////// 630 | game_code(); 631 | slSynch(); 632 | GFS_NwExecOne(file.handle); 633 | GFS_NwGetStat(file.handle, &gfs_svr_status, &byte_dummy); 634 | }while(gfs_svr_status != GFS_SVR_COMPLETED); 635 | file.sectors_read_so_far += file_transfer_sector; 636 | file.transfer_lock = true; 637 | } else if(file.requested && file.sectors_read_so_far >= file.total_sectors) 638 | { 639 | //This branch is for when the file transfer is complete. It also triggers the data handling function. 640 | //The file handler function is a function pointer, not a direct reference, so user can change it when file type changes. 641 | 642 | ///////////////// 643 | file_system_status_reporting = REPORT_CLOSING_FILE; 644 | //game_code(); 645 | finish_file_request(); 646 | file.requested = false; 647 | GFS_Close(file.handle); 648 | //slSynch(); 649 | } else if(!file.requested && file.setup_requested && !adx_stream.file.transfer_lock) 650 | { 651 | //This branch is for when a file is requested, but the file system parameters for that file are not yet set. 652 | //The parameters are set and other important parameters are re-set here, too. 653 | 654 | ////////////////// 655 | if(active_request->immediate_or_async != 0) 656 | { 657 | //Very tough thing. 658 | stm.stopping = true; 659 | stm.restarting = true; 660 | pcm_cease(stm.pcm_num); 661 | stop_adx_stream(); 662 | adx_stream.file.requested = false; 663 | adx_stream.file.transfer_lock = false; 664 | adx_stream.file.setup_requested = false; 665 | adx_stream.active = false; 666 | adx_stream.playing = false; 667 | adx_stream.request_stop = false; 668 | //GFS_Close(adx_stream.file.handle); 669 | file.setup_requested = false; 670 | load_file_list_immediate(); 671 | goto RESTART; //Ugly fix 672 | } else { 673 | file_system_status_reporting = REPORT_SETTING_UP_FILE; 674 | //game_code(); 675 | file.handle = GFS_Open(file.id); 676 | GFS_SetReadPara(file.handle, (64 * 1024)); 677 | GFS_SetTransPara(file.handle, file_transfer_sector); 678 | GFS_SetTmode(file.handle, GFS_TMODE_SDMA0); 679 | GFS_GetFileSize(file.handle, NULL, &file.total_sectors, NULL); 680 | GFS_GetFileInfo(file.handle, NULL, NULL, &file.total_bytes, NULL); 681 | file.sectors_read_so_far = 0; 682 | file.setup_requested = false; 683 | file.requested = true; 684 | file.transfer_lock = true; 685 | } 686 | ////////////////// 687 | //slSynch(); 688 | } else if(adx_stream.file.requested && !adx_stream.back_buffer_filled[0] && !adx_stream.back_buffer_filled[1] 689 | && adx_stream.file.sectors_read_so_far < adx_stream.file.total_sectors && !adx_stream.request_stop) 690 | { 691 | //Basically, we set-up the ADX file, and trigger it to read in new data when the back buffer is empty. 692 | //This branch is for serving an active file request from CD to RAM. 693 | GFS_NwFread(adx_stream.file.handle, file_transfer_sector, 694 | adx_stream.back_buffer[0], file_transfer_size); 695 | do{ 696 | file_system_status_reporting = REPORT_LOADING_ADX; 697 | //slPrint("--iADX--", slLocate(16,2)); 698 | // nbg_sprintf(2, 6, "bytes(%i)", byte_dummy); 699 | // nbg_sprintf(16, 6, "bufrq(%i)", buf.needs_buffer_filled); 700 | // nbg_sprintf(2, 8, "adx0f(%i)", adx_stream.back_buffer_filled[0]); 701 | // nbg_sprintf(2, 9, "adx1f(%i)", adx_stream.back_buffer_filled[1]); 702 | // nbg_sprintf(16, 8, "adxrq(%i)", adx_stream.file.requested); 703 | // nbg_sprintf(16, 9, "atrans(%i)", gfs_svr_status); 704 | // nbg_sprintf(2, 10, "asetup(%i)", adx_stream.file.setup_requested); 705 | // nbg_sprintf(16, 10, "asect(%i)", adx_stream.file.sectors_read_so_far); 706 | // nbg_sprintf(2, 12, "asize(%i)", adx_stream.file.total_bytes); 707 | // nbg_sprintf(16, 12, "ansct(%i)", adx_stream.file.total_sectors); 708 | //////////// 709 | game_code(); 710 | slSynch(); 711 | GFS_NwExecOne(adx_stream.file.handle); 712 | GFS_NwGetStat(adx_stream.file.handle, &gfs_svr_status, &byte_dummy); 713 | }while(gfs_svr_status != GFS_SVR_COMPLETED); 714 | adx_stream.file.sectors_read_so_far += file_transfer_sector; 715 | ////////////////////// 716 | adx_stream.back_buffer_filled[0] = true; 717 | adx_stream.back_buffer_filled[1] = true; 718 | ///////////////////// 719 | // 720 | // In case of the sound not playing yet, this buffer fill was a pre-buffer. 721 | // Now we should start playing the sound. 722 | // 723 | if(!m68k_com->pcmCtrl[adx_stream.pcm_number].sh2_permit && !adx_stream.request_stop) 724 | { 725 | pcm_play(adx_stream.pcm_number, ADX_STREAM, adx_stream.volume); 726 | } 727 | } else if((adx_stream.file.requested && adx_stream.file.sectors_read_so_far >= adx_stream.file.total_sectors) || adx_stream.request_stop) 728 | { 729 | //////////////////////// 730 | //End handling ADX file. The driver should know when to properly stop playback. 731 | 732 | //////////////////////// 733 | file_system_status_reporting = REPORT_CLOSING_FILE; 734 | // game_code(); 735 | adx_stream.file.requested = false; 736 | adx_stream.file.transfer_lock = false; 737 | adx_stream.file.setup_requested = false; 738 | adx_stream.active = false; 739 | adx_stream.playing = false; 740 | adx_stream.request_stop = false; 741 | GFS_Close(adx_stream.file.handle); 742 | // slSynch(); 743 | } else if(!adx_stream.file.requested && adx_stream.file.setup_requested && adx_stream.front_buffer_okay && adx_stream.back_buffer_okay) 744 | { 745 | ///////////////////////////////// 746 | // 747 | // This branch is for when an ADX file is requested, but the file system parameters for it are not yet set. 748 | // This broadly sets up the file system to stream the ADX. It also prepares the driver for the sound. 749 | // 750 | 751 | //////////////////////////// 752 | file_system_status_reporting = REPORT_SETTING_UP_ADX; 753 | // game_code(); 754 | adx_stream.file.handle = GFS_Open(adx_stream.file.id); 755 | GFS_SetReadPara(adx_stream.file.handle, (64 * 1024)); 756 | GFS_SetTransPara(adx_stream.file.handle, file_transfer_sector); 757 | GFS_SetTmode(adx_stream.file.handle, GFS_TMODE_SDMA0); 758 | GFS_GetFileSize(adx_stream.file.handle, NULL, &adx_stream.file.total_sectors, NULL); 759 | GFS_GetFileInfo(adx_stream.file.handle, NULL, NULL, &adx_stream.file.total_bytes, NULL); 760 | adx_stream.file.sectors_read_so_far = 0; 761 | adx_stream.file.setup_requested = false; 762 | adx_stream.file.requested = true; 763 | adx_stream.file.transfer_lock = true; 764 | ////////////////////// 765 | adx_stream.back_buffer_filled[0] = false; 766 | adx_stream.back_buffer_filled[1] = false; 767 | ///////////////////// 768 | // Tell Driver how long to play the sound 769 | // It's approximate, but should be ok. 770 | m68k_com->adx_stream_length = (adx_stream.file.total_bytes / 18)-1; 771 | ///////////////////// 772 | // slSynch(); 773 | } 774 | } else if(buf.needs_buffer_filled && buf.operating) 775 | { 776 | //This branch serves the PCM stream new data when it needs it. 777 | GFS_NwFread(buf.file_handle, buf.transfer_sectors, 778 | buf.segment_locations[buf.active_buf_segment] + (buf.steps_of_new_data_in_buffer * buf.transfer_bytes), buf.transfer_bytes); 779 | buf.steps_of_new_data_in_buffer++; 780 | do{ 781 | 782 | file_system_status_reporting = REPORT_LOADING_PCM; 783 | //slPrint("--MUSI--", slLocate(16,2)); 784 | // nbg_sprintf(2, 5, "steps(%i)", buf.steps_of_new_data_in_buffer); 785 | // nbg_sprintf(2, 6, "bytes(%i)", bytes_read_now); 786 | // nbg_sprintf(16, 5, "act(%i)", buf.active_buf_segment); 787 | // nbg_sprintf(16, 6, "bufrq(%i)", buf.needs_buffer_filled); 788 | // nbg_sprintf(2, 8, "buf0f(%i)", buf.segment_full[0]); 789 | // nbg_sprintf(2, 9, "buf1f(%i)", buf.segment_full[1]); 790 | // nbg_sprintf(2, 10, "buf2f(%i)", buf.segment_full[2]); 791 | // nbg_sprintf(0, 19, "stat(%i)", gfs_svr_status); 792 | 793 | // nbg_sprintf(2, 5, "steps(%i)", buf.vblank_counter); 794 | // nbg_sprintf(2, 6, "bytes(%i)", buf.active_buf_segment); 795 | // nbg_sprintf(2, 8, "buf0f(%i)", buf.segment_full[0]); 796 | // nbg_sprintf(2, 9, "buf1f(%i)", buf.segment_full[1]); 797 | // nbg_sprintf(2, 10, "buf2f(%i)", buf.segment_full[2]); 798 | // nbg_sprintf(16, 8, "buf0t(%i)", buf.segment_refresh_timings[0]); 799 | // nbg_sprintf(16, 9, "buf1t(%i)", buf.segment_refresh_timings[1]); 800 | // nbg_sprintf(16, 10, "buf2t(%i)", buf.segment_refresh_timings[2]); 801 | game_code(); 802 | slSynch(); 803 | if(buf.steps_of_new_data_in_buffer > buf.segment_transfer_time) 804 | { 805 | file.transfer_lock = false; 806 | buf.needs_buffer_filled = false; 807 | buf.segment_full[buf.active_buf_segment] = true; 808 | break; 809 | } 810 | GFS_NwExecOne(buf.file_handle); 811 | GFS_NwGetStat(buf.file_handle, &gfs_svr_status, &bytes_read_now); 812 | }while(gfs_svr_status != GFS_SVR_COMPLETED); 813 | 814 | } 815 | } 816 | 817 | } 818 | 819 | 820 | 821 | -------------------------------------------------------------------------------- /jo_stream_demo/pcmstm.h: -------------------------------------------------------------------------------- 1 | // 2 | //pcmstm.h 3 | // 4 | #ifndef __PCMSTM_H__ 5 | # define __PCMSTM_H__ 6 | 7 | /////////////////////////////////////////////////////////////////////////////// 8 | // PCM Stream stuff 9 | //////////////////////////// 10 | // 11 | // In case of bugs, increase PCM_BUFFERED_BLANKS by 32. 12 | // 13 | #define PCM_BUFFERED_BLANKS (96) 14 | #define NUM_PCM_BUF (PCM_BUFFERED_BLANKS / 48) 15 | //////////////////// 16 | // 17 | #define MAX_FILE_REQUESTS (16) 18 | 19 | #define REPORT_SETTING_UP_PCMSTM (0) 20 | #define REPORT_LOADING_PCM (1) 21 | #define REPORT_SETTING_UP_ADX (2) 22 | #define REPORT_LOADING_ADX (3) 23 | #define REPORT_SETTING_UP_FILE (4) 24 | #define REPORT_LOADING_FILE (5) 25 | #define REPORT_PLAYING_MUSIC (6) 26 | #define REPORT_IDLE (7) 27 | #define REPORT_CLOSING_FILE (8) 28 | 29 | /* 30 | 31 | General addendum: This software-controlled buffer system is much much much slower than CDDA. 32 | With CDDA, you can get 44.1Khz *stereo* 16-bit sound. This can't even get a quarter of that. 33 | At best this system gets a fifth of that. Lol. 34 | But here, the CD system is not locked; you can do multiple things at once. 35 | 36 | So yes, this system is tremendously slower than it could be. 37 | This is because the CD system is only scheduled to be active at the specific point where new data is needed, 38 | and the capability of the CD drive to deliver new data is more or less "near-instant". 39 | Obviously, it isn't near-instant. If you let it pull new data from CD every frame, it can deliver at least two sectors per frame. 40 | 41 | // 42 | // In case of space-optimized version, using PCM_BUFFERED_BLANKS of >>> 96 <<<. 43 | // 44 | Drain time: 96 blanks 45 | Bit rate | Byte rate | Buffer size | File sys margin | Transfer rate (sectors) : (transfer count) 46 | 7680 8: 128 12 KB 84 3 : 2 47 | 16: 256 24 KB 72 4 : 3 48 | 11520 8: 192 18 KB 78 4 : 3 49 | 16: 384 36 KB 60 3 : 6 50 | 15360 8: 256 24 KB 72 4 : 3 51 | 16: 512 48 KB 48 4 : 6 52 | 23040 8: 384 36 KB 60 3 : 6 53 | 16: 768 72 KB 24 4 : 9 Note: No ADX or file stream (Insufficient margin) 54 | 30720 8: 512 48 KB 48 4 : 6 55 | 16: 1024 96 KB 0 4 : 12 (Completely broken...) 56 | (I had a system that could do that, but it had issues..) 57 | */ 58 | 59 | 60 | 61 | /** 62 | In development commentary: 63 | 64 | file handling required structure 65 | 66 | 1 -> User knows where the data from the file is going. 67 | 2 -> User can use a function to specify the function used to handle the data when it is completed. 68 | 3 -> Function handling the request inside the system is needed because of the next point.. 69 | 4 -> System needs to allow queued requests. 70 | 71 | **/ 72 | 73 | typedef struct { 74 | int pcm_num; 75 | int times_to_loop; 76 | int played_blanks; 77 | int total_blanks; 78 | int volume; 79 | Bool playing; 80 | Bool stopping; 81 | Bool restarting; 82 | 83 | } _pcm_stm_param; 84 | 85 | typedef struct { 86 | GfsHn file_handle; 87 | Sint32 file_id; 88 | Sint32 total_bytes; 89 | void * buffer_location_in_sndram; 90 | int steps_of_new_data_in_buffer; 91 | int vblank_counter; 92 | int transfer_bytes; 93 | int transfer_sectors; 94 | int buffer_size_bytes; 95 | int transfer_timing; 96 | Bool operating; 97 | Bool needs_buffer_filled; 98 | Bool setup_requested; 99 | 100 | Bool segment_full[NUM_PCM_BUF]; 101 | void * segment_locations[NUM_PCM_BUF]; 102 | int segment_refresh_timings[NUM_PCM_BUF]; 103 | int active_buf_segment; 104 | int segment_size; 105 | int segment_transfer_time; 106 | 107 | } _pcm_stm_buf_ctrl; 108 | 109 | typedef struct { 110 | GfsHn handle; 111 | Sint32 id; 112 | Sint32 total_bytes; 113 | Sint32 total_sectors; 114 | int sectors_read_so_far; 115 | Bool requested; 116 | Bool setup_requested; 117 | Bool transfer_lock; 118 | void * destination; 119 | } _generic_file_ctrl; 120 | 121 | typedef struct { 122 | _generic_file_ctrl file; 123 | void * front_buffer[2]; 124 | void * back_buffer[2]; 125 | Bool front_buffer_okay; 126 | Bool back_buffer_okay; 127 | Bool front_buffer_filled[2]; 128 | Bool back_buffer_filled[2]; 129 | Bool request_stop; 130 | short volume; 131 | short pcm_number; //Used to fill data for the stream sys to know when to stop playing 132 | Bool active; 133 | Bool playing; 134 | } adx_stream_param; 135 | 136 | // 137 | //Proprietary file stream manager, in case of using dynamic file loading. 138 | #define HANDLE_FILE_ASYNC (0) 139 | #define HANDLE_FILE_ASAP (1) 140 | #define HANDLE_SPECIAL (2) 141 | 142 | typedef struct { 143 | Sint32 id; //File-system ID 144 | void * destination; //Destination address 145 | void (*handler_function)(void *); //Pointer to function used to handle this file 146 | void (*special_handler_function)(Sint32, void *); //Pointer to alternative function used to handle this file 147 | short active; // File request writes "1" when it wants this file served. Manager writes "0" when done. 148 | short done; // File request writes "0" when it wants the file served. Manager writes "1" when done. 149 | short immediate_or_async; // '0' specifies asynchronous. '1' specifies load immediate (stop game, load file, resume game). 150 | } _file_request_entry; 151 | 152 | void new_file_request(Sint8 * filename, void * destination, void (*handler_function)(void *), short immediate_or_async); 153 | void new_special_request(Sint8 * filename, void * destination, void (*handler_function)(Sint32, void *)); 154 | // 155 | // 156 | 157 | extern _pcm_stm_param stm; 158 | extern int file_system_status_reporting; 159 | 160 | void sdrv_stm_vblank_rq(void); 161 | 162 | short add_adx_front_buffer(short bit_rate); 163 | void add_adx_back_buffer(void * t18kb_buffer_location); 164 | void start_adx_stream(Sint8 * filename, short volume); 165 | 166 | void pcm_stream_init(int bitrate, int bit_depth); 167 | void start_pcm_stream(Sint8 * filename, int volume); 168 | void change_pcm_stream_param(char volume, char pan); 169 | void change_adx_stream_param(char volume, char pan); 170 | void stop_adx_stream(void); 171 | void stop_pcm_stream(void); 172 | void pcm_stream_host(void(*game_code)(void)); 173 | 174 | 175 | 176 | #endif 177 | 178 | -------------------------------------------------------------------------------- /jo_stream_demo/pcmsys.c: -------------------------------------------------------------------------------- 1 | //pcm_sys.c 2 | //this file is compiled separately 3 | //hopefully somewhat portable 4 | // 5 | #include //Mostly to link us with SBL file system 6 | #include "pcmsys.h" 7 | #include 8 | #define true (1) 9 | #define false (0) 10 | 11 | static const int logtbl[] = { 12 | /* 0 */ 0, 13 | /* 1 */ 1, 14 | /* 2 */ 2, 2, 15 | /* 4 */ 3, 3, 3, 3, 16 | /* 8 */ 4, 4, 4, 4, 4, 4, 4, 4, 17 | /* 16 */ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 18 | /* 32 */ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 19 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 20 | /* 64 */ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 21 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 22 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 23 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 24 | /* 128 */ 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 25 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 26 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 27 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 28 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 29 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 30 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 31 | 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 32 | }; 33 | 34 | #define PCM_MSK1(a) ((a)&0x0001) 35 | #define PCM_MSK3(a) ((a)&0x0007) 36 | #define PCM_MSK4(a) ((a)&0x000F) 37 | #define PCM_MSK5(a) ((a)&0x001F) 38 | #define PCM_MSK10(a) ((a)&0x03FF) 39 | 40 | #define PCM_SCSP_FREQUENCY (44100L) 41 | 42 | #define PCM_CALC_OCT(smpling_rate) \ 43 | ((int)logtbl[PCM_SCSP_FREQUENCY / ((smpling_rate) + 1)]) 44 | 45 | #define PCM_CALC_SHIFT_FREQ(oct) \ 46 | (PCM_SCSP_FREQUENCY >> (oct)) 47 | 48 | #define PCM_CALC_FNS(smpling_rate, shift_freq) \ 49 | ((((smpling_rate) - (shift_freq)) << 10) / (shift_freq)) 50 | 51 | #define PCM_SET_PITCH_WORD(oct, fns) \ 52 | ((int)((PCM_MSK4(-(oct)) << 11) | PCM_MSK10(fns))) 53 | 54 | sysComPara * m68k_com = (sysComPara *)((SNDPRG + DRV_SYS_END) | 0x20000000); 55 | static unsigned int * scsp_loading_start = (unsigned int*)(0x408 + DRV_SYS_END + 0x20); //Local loading address for sound data, is DRV_SYS_END ahead of the SNDPRG, and ahead of the communication data 56 | unsigned int * scsp_load; 57 | unsigned short * master_volume = (unsigned short *)(SNDRAM + 0x100400); 58 | unsigned short driver_master_volume = 0; 59 | short numberPCMs = 0; 60 | 61 | static short adx_coef_tbl[8][2] = 62 | { 63 | {ADX_768_COEF_1, ADX_768_COEF_2}, 64 | {ADX_1152_COEF_1, ADX_1152_COEF_2}, 65 | {ADX_1536_COEF_1, ADX_1536_COEF_2}, 66 | {ADX_2304_COEF_1, ADX_2304_COEF_2}, 67 | {ADX_640_COEF_1, ADX_640_COEF_2}, 68 | {ADX_960_COEF_1, ADX_960_COEF_2}, 69 | {ADX_1280_COEF_1, ADX_1280_COEF_2}, 70 | {ADX_1920_COEF_1, ADX_1920_COEF_2} 71 | }; 72 | 73 | //MVOL is a 4-bit number; values 0-15 are valid. 74 | //If the value is higher than 15, this will just cap it at 15. 75 | void set_master_volume(unsigned short volume) 76 | { 77 | volume = (volume >= 0xF) ? 0xF : volume; 78 | *master_volume = 0x200 | (volume & 0xF); 79 | driver_master_volume = volume; 80 | } 81 | 82 | void pcm_play(short pcmNumber, char ctrlType, char volume) 83 | { 84 | if(pcmNumber < 0) return; 85 | m68k_com->pcmCtrl[pcmNumber].sh2_permit = 1; 86 | m68k_com->pcmCtrl[pcmNumber].volume = volume; 87 | m68k_com->pcmCtrl[pcmNumber].loopType = ctrlType; 88 | } 89 | 90 | void pcm_parameter_change(short pcmNumber, char volume, char pan) 91 | { 92 | if(pcmNumber < 0) return; 93 | m68k_com->pcmCtrl[pcmNumber].volume = volume; 94 | m68k_com->pcmCtrl[pcmNumber].pan = pan; 95 | } 96 | 97 | void pcm_cease(short pcmNumber) 98 | { 99 | if(pcmNumber < 0) return; 100 | if(m68k_com->pcmCtrl[pcmNumber].loopType <= 0) //If it is a volatile or protected sound, the expected control method is to mute the sound and let it end itself. 101 | { //Protected sounds have a permission state of "until they end". 102 | m68k_com->pcmCtrl[pcmNumber].volume = 0; 103 | } else { 104 | m68k_com->pcmCtrl[pcmNumber].sh2_permit = 0; //If it is a looping sound, the control method is to command it to stop. 105 | } 106 | } 107 | 108 | // 109 | // Usage: 110 | // Intended as the "level reset" function. 111 | // Does not soft or hard reset driver. To do that, re-load the driver binary (run load_drv again). 112 | // This instead resets the loading pointer and number of PCMs to a specific PCM number. 113 | // In use with proper sequence of asset loading, a certain number of sound assets can be retained in sound memory, with others discarded. 114 | // 115 | // The argument "highest_pcm_number_to_keep" is the latest sequentially loaded PCM in sound RAM that signals the point at which: 116 | // Any PCM number loaded earlier than this will be kept in memory and its number still valid to play the sound. 117 | // Any PCM number loaded later than this will be ignored in memory when loading new sounds, but the number is still valid to play sound. 118 | // Pass -1 to this function to clear all PCMs. 119 | void pcm_reset(short highest_pcm_number_to_keep) 120 | { 121 | //For clearing all sounds, input is negative 122 | if(highest_pcm_number_to_keep < 0) { 123 | scsp_load = scsp_loading_start; 124 | numberPCMs = 0; 125 | return; 126 | } 127 | 128 | numberPCMs = highest_pcm_number_to_keep+1; 129 | scsp_load = (unsigned int *)((unsigned int)(m68k_com->pcmCtrl[highest_pcm_number_to_keep].hiAddrBits<<16) | (int)(m68k_com->pcmCtrl[highest_pcm_number_to_keep].loAddrBits)); 130 | if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 2) 131 | { //If this is an ADX sound, offset the loading pointer by # of frames by 18. Address includes 18-byte header offset. 132 | scsp_load = (unsigned int *)((unsigned int)scsp_load + (m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize * 18)); 133 | } else if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 1) 134 | { //If this is an 8-bit PCM, offset the loading pointer by the playsize, exactly (one byte samples). 135 | scsp_load = (unsigned int *)((unsigned int)scsp_load + m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize); 136 | } else if(m68k_com->pcmCtrl[highest_pcm_number_to_keep].bitDepth == 0) 137 | { //If this is a 16-bit PCM, offset the loading pointer by the playsize, shifted left once (two byte samples). 138 | scsp_load = (unsigned int *)((unsigned int)scsp_load + (m68k_com->pcmCtrl[highest_pcm_number_to_keep].playsize<<1)); 139 | } 140 | } 141 | 142 | /**stolen from xl2**/ 143 | #define OPEN_MAX (Sint32)5 144 | #define DIR_MAX (Sint32)1024 145 | #define RD_UNIT (10) 146 | #define SECT_SIZE (2048) 147 | GfsDirTbl gfsDirTbl; 148 | GfsDirName gfsDirName[DIR_MAX]; 149 | Uint32 gfsLibWork[GFS_WORK_SIZE(OPEN_MAX)/sizeof(Uint32)]; 150 | Sint32 gfsDirN; 151 | void cd_init(void) 152 | { 153 | GFS_DIRTBL_TYPE(&gfsDirTbl) = GFS_DIR_NAME; 154 | GFS_DIRTBL_DIRNAME(&gfsDirTbl) = gfsDirName; 155 | GFS_DIRTBL_NDIR(&gfsDirTbl) = DIR_MAX; 156 | gfsDirN = GFS_Init(OPEN_MAX, gfsLibWork, &gfsDirTbl); 157 | } 158 | 159 | void smpc_wait_till_ready (void) 160 | { 161 | // Wait until SF register is cleared 162 | while(SMPC_REG_SF & 0x1) { } 163 | } 164 | 165 | ////////////////////////////////////////////////////////////////////////////// 166 | 167 | void smpc_issue_command(unsigned char cmd) 168 | { 169 | // Set SF register so that no other command can be issued. 170 | SMPC_REG_SF = 1; 171 | 172 | // Writing COMREG starts execution 173 | SMPC_REG_COMREG = cmd; 174 | } 175 | 176 | ////////////////////////////////////////////////////////////////////////////// 177 | 178 | void load_driver_binary(Sint8 * filename, void * buffer, int master_adx_frequency) 179 | { 180 | cd_init(); 181 | GfsHn s_gfs; 182 | Sint32 file_size; 183 | 184 | Sint32 local_name = GFS_NameToId(filename); 185 | 186 | //Open GFS 187 | s_gfs = GFS_Open((Sint32)local_name); 188 | //Get file information (mostly, the file size) 189 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 190 | 191 | GFS_Close(s_gfs); 192 | 193 | GFS_Load(local_name, 0, (Uint32 *)buffer, file_size); 194 | 195 | //The immediacy of these commands is important. 196 | //As per SEGA technical bulletin 51, the Sound CPU is not to be turned off for more than 0.5 seconds. 197 | // Turn off Sound CPU 198 | smpc_issue_command(SMPC_CMD_SNDOFF); 199 | smpc_wait_till_ready(); 200 | //Set max master volume + 4mbit memory 201 | set_master_volume(0xF); 202 | //Copy the driver binary (code) over to sound RAM. The binary includes the vector table information. 203 | slDMACopy(buffer, (void*)SNDRAM, file_size); 204 | slDMAWait(); 205 | //Set the ADX coefficients for the driver to use, if one was selected. 206 | 207 | m68k_com->drv_adx_coef_1 = adx_coef_tbl[master_adx_frequency][0]; 208 | m68k_com->drv_adx_coef_2 = adx_coef_tbl[master_adx_frequency][1]; 209 | 210 | // Turn on Sound CPU again 211 | smpc_wait_till_ready(); 212 | smpc_issue_command(SMPC_CMD_SNDON); 213 | // 214 | } 215 | 216 | void load_drv(int master_adx_frequency) 217 | { 218 | // Make sure SCSP is set to 512k mode 219 | *(unsigned char *)(0x25B00400) = 0x02; 220 | 221 | // Clear Sound Ram 222 | for (int i = 0; i < 0x80000; i+=4){ 223 | *(unsigned int *)(SNDRAM + i) = 0x00000000; 224 | } 225 | // Volatile loading buffer 226 | // This memory is not permanently used, its just a place to temporarily store the driver binary 227 | void * binary_buffer = (void*)2097152; 228 | 229 | // Copy driver over 230 | load_driver_binary((Sint8*)"SDRV.BIN", binary_buffer, master_adx_frequency); 231 | m68k_com->start = 0xFFFF; 232 | volatile int i = 0; 233 | scsp_load = scsp_loading_start; // Re-set loading pointer. 234 | for(i = 0; i < (int)scsp_load; i++) 235 | { 236 | //This is to pop the stack here. Because GCC. 237 | } 238 | //Additionally, reset the number of PCMs. 239 | numberPCMs = 0; 240 | } 241 | 242 | short calculate_bytes_per_blank(int sampleRate, Bool is8Bit, Bool isPAL) 243 | { 244 | int frameCount = (isPAL == true) ? 50 : 60; 245 | int sampleSize = (is8Bit == true) ? 8 : 16; 246 | return ((sampleRate * sampleSize)>>3)/frameCount; 247 | 248 | } 249 | 250 | short convert_bitrate_to_pitchword(short sampleRate) 251 | { 252 | 253 | int octr; 254 | int shiftr; 255 | int fnsr; 256 | 257 | octr = PCM_CALC_OCT(sampleRate); 258 | shiftr = PCM_CALC_SHIFT_FREQ(octr); 259 | fnsr = PCM_CALC_FNS(sampleRate, shiftr); 260 | 261 | return PCM_SET_PITCH_WORD(octr, fnsr); 262 | } 263 | 264 | short load_16bit_pcm(Sint8 * filename, int sampleRate) 265 | { 266 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 267 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 268 | 269 | GfsHn s_gfs; 270 | Sint32 file_size; 271 | 272 | Sint32 local_name = GFS_NameToId(filename); 273 | 274 | //Open GFS 275 | s_gfs = GFS_Open((Sint32)local_name); 276 | //Get file information (mostly, the file size) 277 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 278 | 279 | GFS_Close(s_gfs); 280 | 281 | if(file_size > (128 * 1024)) return -1; //PCM size too large for general-purpose playback [could still work with timed execution & offets] 282 | 283 | file_size += ((unsigned int)file_size & 1) ? 1 : 0; 284 | file_size += ((unsigned int)file_size & 3) ? 2 : 0; 285 | 286 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), file_size); 287 | 288 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)scsp_load >> 16); 289 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)scsp_load & 0xFFFF); 290 | 291 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(sampleRate); 292 | m68k_com->pcmCtrl[numberPCMs].playsize = (file_size>>1); 293 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = calculate_bytes_per_blank(sampleRate, false, PCM_SYS_REGION); //Iniitalize as max volume 294 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_16BIT; //Select 16-bit 295 | m68k_com->pcmCtrl[numberPCMs].loopType = 0; //Initialize as non-looping 296 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Initialize as max volume 297 | 298 | 299 | numberPCMs++; //Increment pcm # 300 | scsp_load = (unsigned int *)((unsigned int )scsp_load + file_size); 301 | return (numberPCMs-1); //Return the PCM # this sound recieved 302 | } 303 | 304 | short load_8bit_pcm(Sint8 * filename, int sampleRate) 305 | { 306 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 307 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 308 | 309 | GfsHn s_gfs; 310 | Sint32 file_size; 311 | 312 | Sint32 local_name = GFS_NameToId(filename); 313 | 314 | //Open GFS 315 | s_gfs = GFS_Open((Sint32)local_name); 316 | //Get file information (mostly, the file size) 317 | GFS_GetFileInfo(s_gfs, NULL, NULL, &file_size, NULL); 318 | 319 | GFS_Close(s_gfs); 320 | 321 | 322 | if(file_size > (64 * 1024)) return -1; //PCM size too large for general-purpose playback [could still work with timed execution & offets] 323 | 324 | 325 | file_size += ((unsigned int)file_size & 1) ? 1 : 0; 326 | file_size += ((unsigned int)file_size & 3) ? 2 : 0; 327 | 328 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), file_size); 329 | 330 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)scsp_load >> 16); 331 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)scsp_load & 0xFFFF); 332 | 333 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(sampleRate); 334 | m68k_com->pcmCtrl[numberPCMs].playsize = (file_size); 335 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = calculate_bytes_per_blank(sampleRate, true, PCM_SYS_REGION); //Iniitalize as max volume 336 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_8BIT; //Select 8-bit 337 | m68k_com->pcmCtrl[numberPCMs].loopType = 0; //Initialize as non-looping 338 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Iniitalize as max volume 339 | 340 | 341 | numberPCMs++; //Increment pcm # 342 | scsp_load = (unsigned int *)((unsigned int )scsp_load + file_size); 343 | return (numberPCMs-1); //Return the PCM # this sound received 344 | } 345 | 346 | // Recursive function to return gcd of a and b 347 | short gcd(short a, short b) 348 | { 349 | if (a == 0) 350 | return b; 351 | return gcd(b % a, a); 352 | } 353 | 354 | // Function to return LCM of two numbers 355 | // Used specifically to find the buffer size for ADX sound effects 356 | short lcm(short a, short b) 357 | { 358 | return (a / gcd(a, b)) * b; 359 | } 360 | 361 | short load_adx(Sint8 * filename) 362 | { 363 | static adx_header adx; 364 | 365 | if( (int)scsp_load > 0x7F800) return -1; //Illegal PCM data address, exit 366 | if( numberPCMs >= PCM_CTRL_MAX) return -1; //Maximum number of PCMs reached, exit 367 | 368 | Sint32 local_name = GFS_NameToId(filename); 369 | 370 | ////////////////////////// 371 | // Step 1: Load the size of the header from the file to the header's location 372 | ////////////////////////// 373 | GFS_Load(local_name, 0, (Uint32 *)&adx, sizeof(adx_header)); 374 | ////////////////////////// 375 | // Step 2: Check the data we just loaded and make sure it's an ADX file. 376 | // If the data does not match what the decompression routine expects, this function will return -1. 377 | ////////////////////////// 378 | // jo_printf(13, 9, "ohalf(%i)", adx.one_half); 379 | // jo_printf(13, 10, "blocksz(%i)", adx.block_size); 380 | // jo_printf(13, 11, "bitdepth(%i)", adx.bit_depth); 381 | // jo_printf(13, 12, "srate(%i)", adx.sample_rate); 382 | if(adx.one_half != 32768 || adx.block_size != 18 || adx.bit_depth != 4) return -1; 383 | ////////////////////////// 384 | // Step 3: Parse the data in the header to the sound driver PCM control struct. 385 | // Special things for ADX is the playsize is actually the # of ADX frames, not the number of samples. 386 | // We expect a block size of 16, so each frame has 16 bytes of sample data which is 32 samples. 387 | ////////////////////////// 388 | // Because we ""dumbly"" load the header to sound RAM, let's give the used pointer the offset. 389 | unsigned int working_address = (unsigned int)(scsp_load) + adx.offset2data + 4; 390 | m68k_com->pcmCtrl[numberPCMs].hiAddrBits = (unsigned short)( (unsigned int)working_address >> 16); 391 | m68k_com->pcmCtrl[numberPCMs].loAddrBits = (unsigned short)( (unsigned int)working_address & 0xFFFF); 392 | m68k_com->pcmCtrl[numberPCMs].pitchword = convert_bitrate_to_pitchword(adx.sample_rate); 393 | m68k_com->pcmCtrl[numberPCMs].playsize = (adx.sample_ct / 32); 394 | short bpb = calculate_bytes_per_blank((int)adx.sample_rate, false, PCM_SYS_REGION); //Iniitalize as max volume 395 | if(bpb != 768 && bpb != 512 && bpb != 384 && bpb != 256 && bpb != 192 && bpb != 128) 396 | { 397 | slPrint("!(ADX INVALID BYTE-RATE)!", slLocate(0, 1)); 398 | return -2; 399 | } 400 | m68k_com->pcmCtrl[numberPCMs].bytes_per_blank = bpb; 401 | unsigned short big_dct_sz = (bpb >= 256) ? lcm(bpb, bpb + 64)<<1 : 5376; // Dirty fix for ultra low bitrate 402 | m68k_com->pcmCtrl[numberPCMs].decompression_size = (big_dct_sz > (adx.sample_ct<<1)) ? adx.sample_ct<<1 : big_dct_sz; 403 | m68k_com->pcmCtrl[numberPCMs].bitDepth = PCM_TYPE_ADX; //Select ADX type 404 | m68k_com->pcmCtrl[numberPCMs].loopType = PCM_SEMI; //Initialize as semi-protected. 405 | m68k_com->pcmCtrl[numberPCMs].volume = 7; //Iniitalize as max volume 406 | numberPCMs++; 407 | ///////////////////////// 408 | // Step 4: Load the compressed ADX data to sound RAM. Unfortunately, we must include the 20 byte header. 409 | // We want to load from an offset location from CD and then load (sample_ct / 32) * 18 bytes. 410 | // We divide the sample count by 32 to retrieve the ADX frame count. We multiply by 18, as that is the size of an ADX 'frame'. 411 | ///////////////////////// 412 | unsigned int number_of_bytes_to_load = (adx.sample_ct / 32) * 18; 413 | number_of_bytes_to_load += ((unsigned int)number_of_bytes_to_load & 1) ? 1 : 0; 414 | number_of_bytes_to_load += ((unsigned int)number_of_bytes_to_load & 3) ? 2 : 0; 415 | GFS_Load(local_name, 0, (Uint32 *)((unsigned int)scsp_load + SNDRAM), number_of_bytes_to_load); 416 | scsp_load = (unsigned int *)((unsigned int )scsp_load + number_of_bytes_to_load); 417 | return (numberPCMs-1); //Return the PCM # this sound recieved 418 | } 419 | 420 | void sdrv_vblank_rq(void) 421 | { 422 | //jo_printf(0, 0, "drv_stat(%i)", m68k_com->start); 423 | m68k_com->start = 1; 424 | } 425 | 426 | //////////////////////////////////////////////////////////////////////////////// 427 | // Redbook Support 428 | // These are mostly CDC commands. 429 | // credit to: ndiddy, ReyeMe, CyberWarriorX [iapetus] 430 | //////////////////////////////////////////////////////////////////////////////// 431 | 432 | 433 | void CDDA_SetVolume(int vol) 434 | { 435 | //Step 1: Remove the volume bits from the value (isolate the pan) 436 | unsigned char newvol = m68k_com->cdda_left_channel_vol_pan & 0x1F; 437 | //Step 2: Apply the volume to the correct bits 438 | newvol |= ((vol & 0x7)<<5); 439 | //Step 3: Apply value back to left channel 440 | m68k_com->cdda_left_channel_vol_pan = newvol; 441 | //Step 4: Repeat for right channel 442 | newvol = m68k_com->cdda_right_channel_vol_pan & 0x1F; 443 | //Step 5: Apply the volume to the correct bits 444 | newvol |= ((vol & 0x7)<<5); 445 | //Step 6: Apply value back to right channel 446 | m68k_com->cdda_right_channel_vol_pan = newvol; 447 | } 448 | 449 | // To see what this does and how to use it, refer to the SCSP manual. 450 | // Warning: Use without reading the manual may break your CD audio. 451 | void CDDA_SetChannelVolPan(unsigned char left_channel, unsigned char right_channel) 452 | { 453 | m68k_com->cdda_left_channel_vol_pan = left_channel; 454 | m68k_com->cdda_right_channel_vol_pan = right_channel; 455 | } 456 | 457 | 458 | // ------------------------------------- 459 | // Functions 460 | // ------------------------------------- 461 | 462 | /** @brief Gets table of contents 463 | * @param toc Table of contents data struct 464 | */ 465 | void CDGetTableOfContents(CDTableOfContents * toc) 466 | { 467 | CDC_TgetToc((Uint32*)toc); 468 | } 469 | 470 | /** @brief Play audio 471 | * @param fromTrack Start track 472 | * @param toTrack End track 473 | * @param loop Loop playback 474 | * @param startAddress Start address of the playback 475 | */ 476 | void CDDA_Play(int fromTrack, int toTrack, Bool loop, int startAddress) 477 | { 478 | CdcPly ply; 479 | 480 | // Get TOC 481 | CDTableOfContents toc; 482 | CDGetTableOfContents(&toc); 483 | 484 | // Start of the playback address 485 | CDC_PLY_STYPE(&ply) = CDC_PTYPE_FAD; // track number 486 | 487 | if (startAddress == 0) 488 | { 489 | 490 | CDC_PLY_SFAD(&ply) = toc.Tracks[fromTrack].fad; 491 | } 492 | else 493 | { 494 | CDC_PLY_SFAD(&ply) = startAddress; 495 | } 496 | 497 | if (toTrack + 1 < MAX_CD_TRACK_COUNT) 498 | { 499 | // End of the playback address 500 | CDC_PLY_ETYPE(&ply) = CDC_PTYPE_FAD; 501 | CDC_PLY_EFAS(&ply) = toc.Tracks[toTrack + 1].fad - toc.Tracks[fromTrack].fad; 502 | } 503 | else 504 | { 505 | // End of the playback is end of the disk 506 | CDC_PLY_ETYPE(&ply) = CDC_PTYPE_DFL; 507 | } 508 | 509 | // Playback mode 510 | CDC_PLY_PMODE(&ply) = CDC_PM_DFL | (loop ? 0xf : 0); // 0xf = infinite repetitions 511 | 512 | // Start playback 513 | CDC_CdPlay(&ply); 514 | } 515 | 516 | /** @brief Stop audio playback 517 | * @return Position of where playback stopped 518 | */ 519 | int CDDA_Stop(void) 520 | { 521 | // Get current address 522 | CdcStat stat; 523 | CDC_GetCurStat(&stat); 524 | 525 | // Restore address 526 | CdcPos poswk; 527 | CDC_POS_PTYPE(&poswk) = CDC_PTYPE_DFL; 528 | CDC_CdSeek(&poswk); 529 | 530 | // Last playback address 531 | return stat.report.fad; 532 | } 533 | 534 | void CDDA_PlaySingle(int track, Bool loop) 535 | { 536 | CDDA_Play(track, track, loop, 0); 537 | } 538 | 539 | 540 | -------------------------------------------------------------------------------- /jo_stream_demo/pcmsys.h: -------------------------------------------------------------------------------- 1 | // 2 | //pcmsys.h 3 | // 4 | #ifndef __PCMSYS_H__ 5 | # define __PCMSYS_H__ 6 | 7 | /////////////// 8 | // Likely duplicates from other libraries (in this case, taken from iapetus) 9 | ////////////////////////////////////////////////////////////////////////////// 10 | 11 | #define SMPC_REG_IREG(i) *((volatile unsigned char *)0x20100001+((i) * 2)) 12 | #define SMPC_REG_COMREG *((volatile unsigned char *)0x2010001F) 13 | #define SMPC_REG_OREG(o) *((volatile unsigned char *)0x20100021+((o) * 2)) 14 | #define SMPC_REG_SR *((volatile unsigned char *)0x20100061) 15 | #define SMPC_REG_SF *((volatile unsigned char *)0x20100063) 16 | #define SMPC_REG_PDR1 *((volatile unsigned char *)0x20100075) 17 | #define SMPC_REG_PDR2 *((volatile unsigned char *)0x20100077) 18 | #define SMPC_REG_DDR1 *((volatile unsigned char *)0x20100079) 19 | #define SMPC_REG_DDR2 *((volatile unsigned char *)0x2010007B) 20 | #define SMPC_REG_IOSEL *((volatile unsigned char *)0x2010007D) 21 | #define SMPC_REG_EXLE *((volatile unsigned char *)0x2010007F) 22 | 23 | ////////////////////////////////////////////////////////////////////////////// 24 | 25 | #define SMPC_CMD_MSHON 0x00 26 | #define SMPC_CMD_SSHON 0x02 27 | #define SMPC_CMD_SSHOFF 0x03 28 | #define SMPC_CMD_SNDON 0x06 29 | #define SMPC_CMD_SNDOFF 0x07 30 | #define SMPC_CMD_CDON 0x08 31 | #define SMPC_CMD_CDOFF 0x09 32 | #define SMPC_CMD_CARTON 0x0A 33 | #define SMPC_CMD_CARTOFF 0x0B 34 | #define SMPC_CMD_SYSRES 0x0D 35 | #define SMPC_CMD_CKCHG352 0x0E 36 | #define SMPC_CMD_CKCHG320 0x0F 37 | #define SMPC_CMD_INTBACK 0x10 38 | #define SMPC_CMD_SETTIM 0x16 39 | #define SMPC_CMD_SETSM 0x17 40 | #define SMPC_CMD_NMIREQ 0x18 41 | #define SMPC_CMD_RESENA 0x19 42 | #define SMPC_CMD_RESDIS 0x1A 43 | 44 | ////////////////////////////////////////////////////////////////////////////// 45 | //Address mapping shorthand 46 | #define MAP_TO_SCSP(sh2map_snd_adr) ((sh2map_snd_adr - SNDRAM)) 47 | ////////////////////////////////////////////////////////////////////////////// 48 | //No Touchy Sound Ram Start! 49 | #define SNDRAM (631242752) 50 | // 1KB here is reserved for interrupts 51 | #define SNDPRG (SNDRAM + 0x408) 52 | //Also the end of sound RAM 53 | #define PCMEND (SNDRAM + 0x7F000) 54 | ////////////////////////////////////////////////////////////////////////////// 55 | #define DRV_SYS_END (47 * 1024) //System defined safe end of driver's address space 56 | #define PCM_CTRL_MAX (93) 57 | ////////////////////////////////////////////////////////////////////////////// 58 | #define PCM_ALT_LOOP (3) 59 | #define PCM_RVS_LOOP (2) 60 | #define PCM_FWD_LOOP (1) 61 | #define PCM_VOLATILE (0) 62 | #define PCM_PROTECTED (-1) 63 | #define PCM_SEMI (-2) 64 | #define ADX_STREAM (-3) 65 | ////////////////////////////////////////////////////////////////////////////// 66 | #define PCM_TYPE_ADX (2) // 4-bit (compressed audio) 67 | #define PCM_TYPE_8BIT (1) // 8-bit 68 | #define PCM_TYPE_16BIT (0) // 16-bit 69 | ////////////////////////////////////////////////////////////////////////////// 70 | #define PCM_SYS_REGION (0) //0 for NTSC, 1 for PAL 71 | ////////////////////////////////////////////////////////////////////////////// 72 | #define PCM_PAN_LEFT (1<<4) 73 | #define PCM_PAN_RIGHT (0) 74 | ////////////////////////////////////////////////////////////////////////////// 75 | #define ADX_MASTER_768 (0) 76 | #define ADX_MASTER_1152 (1) 77 | #define ADX_MASTER_1536 (2) 78 | #define ADX_MASTER_2304 (3) 79 | /* 7.68 Data */ 80 | #define ADX_768_COEF_1 (4401) 81 | #define ADX_768_COEF_2 (-1183) 82 | /* 11.52 data */ 83 | #define ADX_1152_COEF_1 (5386) 84 | #define ADX_1152_COEF_2 (-1771) 85 | /* 15.36 data */ 86 | #define ADX_1536_COEF_1 (5972) 87 | #define ADX_1536_COEF_2 (-2187) 88 | /* 23.04 data */ 89 | #define ADX_2304_COEF_1 (6631) 90 | #define ADX_2304_COEF_2 (-2685) 91 | ////////////////////////////////////////////////////////////////////////////// 92 | // 6400 Hz for PAL 93 | #define ADX_PAL_640 (4) 94 | #define ADX_640_COEF_1 (3915) 95 | #define ADX_640_COEF_2 (-936) 96 | // 9600 Hz for PAL 97 | #define ADX_PAL_960 (5) 98 | #define ADX_960_COEF_1 (4963) 99 | #define ADX_960_COEF_2 (-1504) 100 | // 12800 Hz for PAL 101 | #define ADX_PAL_1280 (6) 102 | #define ADX_1280_COEF_1 (5612) 103 | #define ADX_1280_COEF_2 (-1923) 104 | // 19200 Hz for PAL 105 | #define ADX_PAL_1920 (7) 106 | #define ADX_1920_COEF_1 (6359) 107 | #define ADX_1920_COEF_2 (-2469) 108 | 109 | 110 | typedef struct { 111 | char loopType; //[0,1,2,3] No loop, normal loop, reverse loop, alternating loop 112 | unsigned char bitDepth; //0, 1, or 2; 0 is 16-bit, 1 is 8-bit, 2 is ADX 113 | unsigned short hiAddrBits; //bits 19-16 of... 114 | unsigned short loAddrBits; //Two 16-bit chunks that when combined, form the start address of the sound. 115 | unsigned short LSA; //The # of samples forward from the start address to return to after loop. 116 | unsigned short playsize; //The # of samples to play before the sound shall loop. **Otherwise used as the length of the sound.** Do not leave at 0! 117 | //8 bit PCM is 1 byte per sample. 16 bit PCM is 2 bytes per sample. Therefore an 8bit PCM is a maximum of 64KB, and 16bit is 128KB. 118 | unsigned short pitchword; //the OCT & FNS word to use in the ICSR, verbatim. 119 | unsigned char pan; //Direct pan setting 120 | unsigned char volume; //Direct volume setting 121 | unsigned short bytes_per_blank; //Bytes the PCM will play every time the driver is run (vblank) 122 | unsigned short decompression_size; //Size of the buffer used for an ADX sound effect. Specifically sized by Master SH2. 123 | unsigned char sh2_permit; //Does the SH2 permit this command? If TRUE, run the command. If FALSE, key its ICSR OFF. 124 | char icsr_target; //Which explicit ICSR is this to land in? Can be controlled by SH2 or by driver. 125 | } _PCM_CTRL; //Driver Local Command Struct 126 | 127 | typedef struct { 128 | volatile unsigned int adx_stream_length; //Length of the ADX stream (in ADX frames) 129 | volatile unsigned short start; //System Start Boolean 130 | volatile char adx_buffer_pass[2]; //Booleans 131 | volatile short drv_adx_coef_1; //The (signed!) coefficient 1 the driver will use to build ADX multiplication tables. 132 | volatile short drv_adx_coef_2; //The (signed!) coefficient 2 the driver will use to build ADX multiplication tables. 133 | volatile _PCM_CTRL * pcmCtrl; 134 | volatile unsigned char cdda_left_channel_vol_pan; // Redbook left channel volume & pan. 135 | volatile unsigned char cdda_right_channel_vol_pan; // Redbook right channel volume & pan. 136 | } sysComPara; 137 | 138 | 139 | typedef struct { 140 | unsigned short one_half; //[this is 32768 or 0x8000] 141 | short offset2data; 142 | unsigned char format; //[this is 3] 143 | unsigned char block_size; //[this is 18] 144 | unsigned char bit_depth; //[this is 4] 145 | unsigned char channels; //[this should be 1] 146 | unsigned int sample_rate; 147 | unsigned int sample_ct; 148 | unsigned short hp_cutoff; //[this should be 500] 149 | unsigned char loop; 150 | unsigned char illegal; //[Boolean, 0 for false, 1 for true] 151 | } adx_header; 152 | 153 | // 154 | 155 | // 156 | extern sysComPara * m68k_com; 157 | extern unsigned int * scsp_load; 158 | extern unsigned short * master_volume; 159 | extern unsigned short driver_master_volume; 160 | extern short numberPCMs; 161 | 162 | // 163 | // System functions shared for pcmstm.c/h 164 | // 165 | short convert_bitrate_to_pitchword(short sampleRate); 166 | short calculate_bytes_per_blank(int sampleRate, Bool is8Bit, Bool isPAL); 167 | short lcm(short a, short b); 168 | void cd_init(void); 169 | 170 | // 171 | // These are likely to be duplicate commands from other libraries. 172 | // 173 | void smpc_wait_till_ready(void); 174 | void smpc_issue_command(unsigned char cmd); 175 | // 176 | // 177 | // 178 | 179 | short load_16bit_pcm(Sint8 * filename, int sampleRate); 180 | short load_8bit_pcm(Sint8 * filename, int sampleRate); 181 | short load_adx(Sint8 * filename); 182 | void load_drv(int master_adx_frequency); 183 | 184 | void set_master_volume(unsigned short volume); 185 | void pcm_play(short pcmNumber, char ctrlType, char volume); 186 | void pcm_parameter_change(short pcmNumber, char volume, char pan); 187 | void pcm_cease(short pcmNumber); 188 | 189 | // 190 | // Usage: 191 | // Intended as the "level reset" function. 192 | // Does not soft or hard reset driver. To do that, re-load the driver binary (run load_drv again). 193 | // This instead resets the loading pointer and number of PCMs to a specific PCM number. 194 | // In use with proper sequence of asset loading, a certain number of sound assets can be retained in sound memory, with others discarded. 195 | // 196 | // The argument "highest_pcm_number_to_keep" is the latest sequentially loaded PCM in sound RAM that signals the point at which: 197 | // Any PCM number loaded earlier than this will be kept in memory and its number still valid to play the sound. 198 | // Any PCM number loaded later than this will be ignored in memory when loading new sounds, but the number is still valid to play sound. 199 | void pcm_reset(short default_pcm_count); 200 | 201 | void sdrv_vblank_rq(void); 202 | 203 | // 204 | // Redbook support 205 | // Credit: ndiddy, ReyeMe, CyberWarriorX [Iapetus] 206 | // 207 | 208 | // ------------------------------------- 209 | // Types 210 | // ------------------------------------- 211 | 212 | /** @brief Track location data 213 | */ 214 | typedef struct 215 | { 216 | unsigned int Control:4; 217 | unsigned int Number:4; 218 | unsigned int fad:24; 219 | } CDTrackLocation; 220 | 221 | /** @brief Track information data 222 | */ 223 | typedef struct 224 | { 225 | unsigned char Control:4; 226 | unsigned char Address:4; 227 | unsigned char Number; 228 | union { 229 | short point; 230 | struct { 231 | char psec; 232 | char pframe; 233 | } pData; 234 | 235 | }pBody; 236 | 237 | } CDTrackInformation; 238 | 239 | /** @brief Session data 240 | */ 241 | typedef struct 242 | { 243 | unsigned int Control:4; 244 | unsigned int Address:4; 245 | unsigned int fad:24; 246 | } CDSession; 247 | 248 | /** @brief Table of contents 249 | */ 250 | #define MAX_CD_TRACK_COUNT (24) 251 | typedef struct 252 | { 253 | CDTrackLocation Tracks[MAX_CD_TRACK_COUNT]; 254 | CDTrackInformation FirstTrack; 255 | CDTrackInformation LastTrack; 256 | CDSession Session; 257 | } CDTableOfContents; 258 | 259 | 260 | void CDDA_SetVolume(int vol); 261 | void CDDA_SetChannelVolPan(unsigned char left_channel, unsigned char right_channel); 262 | void CDDA_Play(int fromTrack, int toTrack, Bool loop, int startAddress); 263 | void CDDA_PlaySingle(int track, Bool loop); 264 | int CDDA_Stop(void); 265 | 266 | 267 | #endif 268 | 269 | -------------------------------------------------------------------------------- /jo_stream_demo/run_with_daemon_tools_and_ssf.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | 3 | SET EMULATOR_DIR=..\..\Emulators 4 | SET DT_DIR=C:\Program Files (x86)\DAEMON Tools Lite 5 | 6 | if exist game.iso ( 7 | echo Mounting image... 8 | "%DT_DIR%\DTLite.exe" -mount 0,game.cue 9 | cd "%EMULATOR_DIR%\SSF\" 10 | echo Running SSF... 11 | "SSF.exe" 12 | echo Unmounting image... 13 | "%DT_DIR%\DTLite.exe" -unmount 0 14 | ) else ( 15 | echo Please compile first ! 16 | pause 17 | ) 18 | -------------------------------------------------------------------------------- /jo_stream_demo/run_with_mednafen.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET MEDNAFEN_EXECUTABLE_PATH=%EMULATOR_DIR%\mednafen\mednafen.exe 4 | 5 | if not exist %MEDNAFEN_EXECUTABLE_PATH% ( 6 | echo --- 7 | echo Please install Mednafen here %EMULATOR_DIR% 8 | echo --- 9 | pause 10 | exit 11 | ) 12 | 13 | if exist game.cue ( 14 | "%MEDNAFEN_EXECUTABLE_PATH%" "%cd%\game.cue" -sound.volume "150" 15 | ) else ( 16 | echo Please compile first ! 17 | ) 18 | -------------------------------------------------------------------------------- /jo_stream_demo/run_with_nova.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET NOVA_BIOS_PATH=%EMULATOR_DIR%\nova\bios\bios.bin 4 | 5 | if not exist %NOVA_BIOS_PATH% ( 6 | echo --- 7 | echo Nova doesn't support HLE bios today. 8 | echo Therefore, please put Sega Saturn bios here %NOVA_BIOS_PATH% 9 | echo --- 10 | pause 11 | exit 12 | ) 13 | 14 | if exist game.iso ( 15 | "%EMULATOR_DIR%\nova\nova.exe" "%cd%\game.cue" 16 | ) else ( 17 | echo Please compile first ! 18 | ) 19 | -------------------------------------------------------------------------------- /jo_stream_demo/run_with_powershell_and_ssf.ps1: -------------------------------------------------------------------------------- 1 | # /!\ THIS SCRIPT DOESN'T WORK TODAY BECAUSE OF SSF /!\ 2 | $isoPath = Join-Path $pwd.Path game.iso 3 | $ssfDirectory = Join-Path $pwd.Path "..\..\Emulators\SSF\" 4 | Write-Host "Mounting image..." 5 | Mount-DiskImage -StorageType ISO -ImagePath $isoPath 6 | Write-Host "Running SSF..." 7 | cd "$ssfDirectory" 8 | Start-Process -FilePath "SSF.exe" -Wait 9 | Write-Host "Unmounting image..." 10 | Get-DiskImage $isoPath | Dismount-DiskImage 11 | -------------------------------------------------------------------------------- /jo_stream_demo/run_with_virtual_clone_drive_and_ssf.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET VCD_DIR=C:\Program Files (x86)\Elaborate Bytes\VirtualCloneDrive 4 | 5 | if exist game.iso ( 6 | echo Mounting image... 7 | "%VCD_DIR%\vcdmount.exe" game.iso 8 | cd "%EMULATOR_DIR%\SSF\" 9 | echo Running SSF... 10 | "SSF.exe" 11 | echo Unmounting image... 12 | "%VCD_DIR%\vcdmount.exe" /u 13 | ) else ( 14 | echo Please compile first ! 15 | pause 16 | ) 17 | -------------------------------------------------------------------------------- /jo_stream_demo/run_with_yabaSanshiro.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | 4 | if exist game.iso ( 5 | echo Please wait, it's very slow, but I don't know why... 6 | "%EMULATOR_DIR%\YabaSanshiro\yabasanshiro.exe" -a -i game.iso 7 | ) else ( 8 | echo Please compile first ! 9 | ) 10 | -------------------------------------------------------------------------------- /jo_stream_demo/run_with_yabause.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | 4 | if exist game.iso ( 5 | "%EMULATOR_DIR%\yabause\yabause.exe" -a -i game.iso 6 | ) else ( 7 | echo Please compile first ! 8 | ) 9 | -------------------------------------------------------------------------------- /jo_stream_demo/ssf.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | SET EMULATOR_DIR=..\..\Emulators 3 | SET VCD_DIR=C:\Program Files (x86)\Elaborate Bytes\VirtualCloneDrive 4 | 5 | if exist game.iso ( 6 | echo Mounting image... 7 | "%VCD_DIR%\vcdmount.exe" game.iso 8 | cd "%EMULATOR_DIR%\SSF\" 9 | echo Running SSF... 10 | "SSF.exe" 11 | echo Unmounting image... 12 | "%VCD_DIR%\vcdmount.exe" /u 13 | ) else ( 14 | echo Please compile first ! 15 | pause 16 | ) 17 | -------------------------------------------------------------------------------- /jo_stream_demo/use_mednafen.bat: -------------------------------------------------------------------------------- 1 | @ECHO Off 2 | 3 | if exist game.iso ( 4 | "D:\games\med\mednafen.exe" game.cue 5 | ) else ( 6 | echo Please compile first ! 7 | ) -------------------------------------------------------------------------------- /logo_by_vbt_toaster2000.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/logo_by_vbt_toaster2000.png -------------------------------------------------------------------------------- /m68k-elf/ar.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/ar.exe -------------------------------------------------------------------------------- /m68k-elf/as.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/as.exe -------------------------------------------------------------------------------- /m68k-elf/cc1.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/cc1.exe -------------------------------------------------------------------------------- /m68k-elf/cc1plus.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/cc1plus.exe -------------------------------------------------------------------------------- /m68k-elf/collect2.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/collect2.exe -------------------------------------------------------------------------------- /m68k-elf/g++-mapper-server.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/g++-mapper-server.exe -------------------------------------------------------------------------------- /m68k-elf/iconv.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/iconv.dll -------------------------------------------------------------------------------- /m68k-elf/ld.bfd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/ld.bfd.exe -------------------------------------------------------------------------------- /m68k-elf/ld.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/ld.exe -------------------------------------------------------------------------------- /m68k-elf/liblto_plugin.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/liblto_plugin.dll -------------------------------------------------------------------------------- /m68k-elf/liblto_plugin.dll.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/liblto_plugin.dll.a -------------------------------------------------------------------------------- /m68k-elf/liblto_plugin.la: -------------------------------------------------------------------------------- 1 | # liblto_plugin.la - a libtool library file 2 | # Generated by libtool (GNU libtool 1.3134 2009-11-29) 2.2.7a 3 | # 4 | # Please DO NOT delete this file! 5 | # It is necessary for linking the library. 6 | 7 | # The name that we can dlopen(3). 8 | dlname='liblto_plugin.dll' 9 | 10 | # Names of this library. 11 | library_names='liblto_plugin.dll.a' 12 | 13 | # The name of the static archive. 14 | old_library='' 15 | 16 | # Linker flags that can not go in dependency_libs. 17 | inherited_linker_flags='' 18 | 19 | # Libraries that this one depends upon. 20 | dependency_libs='' 21 | 22 | # Names of additional weak libraries provided by this library 23 | weak_library_names='' 24 | 25 | # Version information for liblto_plugin. 26 | current=0 27 | age=0 28 | revision=0 29 | 30 | # Is this an already installed library? 31 | installed=yes 32 | 33 | # Should we warn about portability when linking against -modules? 34 | shouldnotlink=yes 35 | 36 | # Files to dlopen/dlpreopen 37 | dlopen='' 38 | dlpreopen='' 39 | 40 | # Directory that this library needs to be installed in: 41 | libdir='/home/cross-gcc/11.1.0/m68k-elf/libexec/gcc/m68k-elf/11.1.0' 42 | -------------------------------------------------------------------------------- /m68k-elf/libwinpthread-1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/libwinpthread-1.dll -------------------------------------------------------------------------------- /m68k-elf/lto-wrapper.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/lto-wrapper.exe -------------------------------------------------------------------------------- /m68k-elf/lto1.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/lto1.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-addr2line.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-addr2line.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-ar.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-ar.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-as.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-as.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-c++.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-c++.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-c++filt.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-c++filt.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-cpp.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-cpp.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-elfedit.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-elfedit.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-g++.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-g++.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gcc-11.1.0.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gcc-11.1.0.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gcc-ar.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gcc-ar.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gcc-nm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gcc-nm.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gcc-ranlib.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gcc-ranlib.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gcc.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gcc.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gcov-dump.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gcov-dump.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gcov-tool.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gcov-tool.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gcov.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gcov.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gdb-add-index: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | # Add a .gdb_index section to a file. 4 | 5 | # Copyright (C) 2010-2021 Free Software Foundation, Inc. 6 | # This program is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this program. If not, see . 18 | 19 | # This program assumes gdb and objcopy are in $PATH. 20 | # If not, or you want others, pass the following in the environment 21 | GDB=${GDB:=gdb} 22 | OBJCOPY=${OBJCOPY:=objcopy} 23 | READELF=${READELF:=readelf} 24 | 25 | myname="${0##*/}" 26 | 27 | dwarf5="" 28 | if [ "$1" = "-dwarf-5" ]; then 29 | dwarf5="$1" 30 | shift 31 | fi 32 | 33 | if test $# != 1; then 34 | echo "usage: $myname [-dwarf-5] FILE" 1>&2 35 | exit 1 36 | fi 37 | 38 | file="$1" 39 | 40 | if test ! -r "$file"; then 41 | echo "$myname: unable to access: $file" 1>&2 42 | exit 1 43 | fi 44 | 45 | dir="${file%/*}" 46 | test "$dir" = "$file" && dir="." 47 | 48 | dwz_file="" 49 | if $READELF -S "$file" | grep -q " \.gnu_debugaltlink "; then 50 | dwz_file=$($READELF --string-dump=.gnu_debugaltlink "$file" \ 51 | | grep -A1 "'\.gnu_debugaltlink':" \ 52 | | tail -n +2 \ 53 | | sed 's/.*]//') 54 | dwz_file=$(echo $dwz_file) 55 | if $READELF -S "$dwz_file" | grep -E -q " \.(gdb_index|debug_names) "; then 56 | # Already has an index, skip it. 57 | dwz_file="" 58 | fi 59 | fi 60 | 61 | set_files () 62 | { 63 | local file="$1" 64 | 65 | index4="${file}.gdb-index" 66 | index5="${file}.debug_names" 67 | debugstr="${file}.debug_str" 68 | debugstrmerge="${file}.debug_str.merge" 69 | debugstrerr="${file}.debug_str.err" 70 | } 71 | 72 | tmp_files= 73 | for f in "$file" "$dwz_file"; do 74 | if [ "$f" = "" ]; then 75 | continue 76 | fi 77 | set_files "$f" 78 | tmp_files="$tmp_files $index4 $index5 $debugstr $debugstrmerge $debugstrerr" 79 | done 80 | 81 | rm -f $tmp_files 82 | 83 | # Ensure intermediate index file is removed when we exit. 84 | trap "rm -f $tmp_files" 0 85 | 86 | $GDB --batch -nx -iex 'set auto-load no' \ 87 | -ex "file $file" -ex "save gdb-index $dwarf5 $dir" || { 88 | # Just in case. 89 | status=$? 90 | echo "$myname: gdb error generating index for $file" 1>&2 91 | exit $status 92 | } 93 | 94 | # In some situations gdb can exit without creating an index. This is 95 | # not an error. 96 | # E.g., if $file is stripped. This behaviour is akin to stripping an 97 | # already stripped binary, it's a no-op. 98 | status=0 99 | 100 | handle_file () 101 | { 102 | local file 103 | file="$1" 104 | 105 | set_files "$file" 106 | 107 | if test -f "$index4" -a -f "$index5"; then 108 | echo "$myname: Both index types were created for $file" 1>&2 109 | status=1 110 | elif test -f "$index4" -o -f "$index5"; then 111 | if test -f "$index4"; then 112 | index="$index4" 113 | section=".gdb_index" 114 | else 115 | index="$index5" 116 | section=".debug_names" 117 | fi 118 | debugstradd=false 119 | debugstrupdate=false 120 | if test -s "$debugstr"; then 121 | if ! $OBJCOPY --dump-section .debug_str="$debugstrmerge" "$file" \ 122 | /dev/null 2>$debugstrerr; then 123 | cat >&2 $debugstrerr 124 | exit 1 125 | fi 126 | if grep -q "can't dump section '.debug_str' - it does not exist" \ 127 | $debugstrerr; then 128 | debugstradd=true 129 | else 130 | debugstrupdate=true 131 | cat >&2 $debugstrerr 132 | fi 133 | cat "$debugstr" >>"$debugstrmerge" 134 | fi 135 | 136 | $OBJCOPY --add-section $section="$index" \ 137 | --set-section-flags $section=readonly \ 138 | $(if $debugstradd; then \ 139 | echo --add-section .debug_str="$debugstrmerge"; \ 140 | echo --set-section-flags .debug_str=readonly; \ 141 | fi; \ 142 | if $debugstrupdate; then \ 143 | echo --update-section .debug_str="$debugstrmerge"; \ 144 | fi) \ 145 | "$file" "$file" 146 | 147 | status=$? 148 | else 149 | echo "$myname: No index was created for $file" 1>&2 150 | echo "$myname: [Was there no debuginfo? Was there already an index?]" \ 151 | 1>&2 152 | fi 153 | } 154 | 155 | handle_file "$file" 156 | if [ "$dwz_file" != "" ]; then 157 | handle_file "$dwz_file" 158 | fi 159 | 160 | exit $status 161 | -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gdb.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gdb.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-gprof.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-gprof.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-ld.bfd.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-ld.bfd.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-ld.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-ld.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-lto-dump.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-lto-dump.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-nm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-nm.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-objcopy.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-objcopy.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-objdump.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-objdump.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-ranlib.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-ranlib.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-readelf.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-readelf.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-size.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-size.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-strings.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-strings.exe -------------------------------------------------------------------------------- /m68k-elf/m68k-elf-strip.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/m68k-elf-strip.exe -------------------------------------------------------------------------------- /m68k-elf/make.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/make.exe -------------------------------------------------------------------------------- /m68k-elf/nm.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/nm.exe -------------------------------------------------------------------------------- /m68k-elf/objcopy.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/objcopy.exe -------------------------------------------------------------------------------- /m68k-elf/objdump.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/objdump.exe -------------------------------------------------------------------------------- /m68k-elf/ranlib.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/ranlib.exe -------------------------------------------------------------------------------- /m68k-elf/readelf.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/readelf.exe -------------------------------------------------------------------------------- /m68k-elf/strip.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/m68k-elf/strip.exe -------------------------------------------------------------------------------- /small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ponut64/SCSP_poneSound/31782e4c61337327f23eb9aa45ecd37fe0944ea0/small.png --------------------------------------------------------------------------------