1 /****************************************************************************
2  *  gx_audio.c
3  *
4  *  Genesis Plus GX audio support
5  *
6  *  Copyright Eke-Eke (2007-2015), based on original work from Softdev (2006)
7  *
8  *  Redistribution and use of this code or any derivative works are permitted
9  *  provided that the following conditions are met:
10  *
11  *   - Redistributions may not be sold, nor may they be used in a commercial
12  *     product or activity.
13  *
14  *   - Redistributions that are modified from the original source must include the
15  *     complete source code, including the source code for all components used by a
16  *     binary built from the modified sources. However, as a special exception, the
17  *     source code distributed need not include anything that is normally distributed
18  *     (in either source or binary form) with the major components (compiler, kernel,
19  *     and so on) of the operating system on which the executable runs, unless that
20  *     component itself accompanies the executable.
21  *
22  *   - Redistributions must reproduce the above copyright notice, this list of
23  *     conditions and the following disclaimer in the documentation and/or other
24  *     materials provided with the distribution.
25  *
26  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
30  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  *  POSSIBILITY OF SUCH DAMAGE.
37  *
38  ****************************************************************************************/
39 
40 #include "shared.h"
41 
42 /* Length is dimensionned for at least one frame of emulation */
43 #define SOUND_BUFFER_LEN 4096
44 
45 /* Number of sound buffers */
46 #define SOUND_BUFFER_NUM 3
47 
48 /* DMA soundbuffers (required to be 32-bytes aligned) */
49 static u8 soundbuffer[SOUND_BUFFER_NUM][SOUND_BUFFER_LEN] ATTRIBUTE_ALIGN(32);
50 
51 /* Current work soundbuffer */
52 static int bufferIndex;
53 static int bufferSize;
54 
55 /* Background music */
56 static u8 *Bg_music_ogg = NULL;
57 static u32 Bg_music_ogg_size = 0;
58 
59 /* Frame Sync */
60 u32 audioSync;
61 static u32 audioWait;
62 
63 /***************************************************************************************/
64 /*   Audio engine                                                                      */
65 /***************************************************************************************/
66 
67 /* Audio DMA callback */
ai_callback(void)68 static void ai_callback(void)
69 {
70   audioWait = 0;
71 }
72 
73 /* AUDIO engine initialization */
gx_audio_Init(void)74 void gx_audio_Init(void)
75 {
76   /* Initialize AUDIO processing library (ASNDLIB) */
77   /* AUDIO & DSP hardware are initialized */
78   /* Default samplerate is set to 48kHz */
79   ASND_Init();
80 
81   /* Load background music from FAT device */
82   char fname[MAXPATHLEN];
83   sprintf(fname,"%s/Bg_music.ogg",DEFAULT_PATH);
84   FILE *f = fopen(fname,"rb");
85   if (f)
86   {
87     struct stat filestat;
88     stat(fname, &filestat);
89     Bg_music_ogg_size = filestat.st_size;
90     Bg_music_ogg = memalign(32,Bg_music_ogg_size);
91     if (Bg_music_ogg)
92     {
93       fread(Bg_music_ogg,1,Bg_music_ogg_size,f);
94     }
95     fclose(f);
96   }
97 }
98 
99 /* AUDIO engine shutdown */
gx_audio_Shutdown(void)100 void gx_audio_Shutdown(void)
101 {
102   PauseOgg(1);
103   StopOgg();
104   ASND_Pause(1);
105   ASND_End();
106   if (Bg_music_ogg)
107   {
108     free(Bg_music_ogg);
109   }
110 }
111 
112 /***
113       gx_audio_Update
114 
115      This function retrieves samples for the frame then set the next DMA parameters
116      Parameters will be taken in account only when current DMA operation is over
117 
118      To keep audio & video synchronized, DMA from external memory to audio interface is
119      started once by video update function when first frame is ready to be displayed,
120      then anytime video mode is changed and emulation resynchronized to video hardware.
121 
122      Once started, audio DMA restarts automatically when all samples have been played.
123      At that time:
124        - if DMA settings have not been updated, previous sound buffer will be played again
125        - if DMA settings are updated too fast, one sound buffer frame might be skipped
126 
127      Therefore, in order to maintain perfect audio playback without any sound skipping
128      or lagging, we need to make sure frame emulation is completed and this function is
129      called before previous DMA transfer is finished and after it has been started.
130 
131      This is done by synchronizing frame emulation with audio DMA interrupt (which happens
132      anytime audio DMA restarts). When video sync is enabled, to keep emulation in sync
133      with both video AND audio, an appropriate number of samples is rendered per frame by
134      adjusting emulator output samplerate.
135  ***/
gx_audio_Update(int status)136 int gx_audio_Update(int status)
137 {
138   /* Current available soundbuffer */
139   s16 *sb = (s16 *)(soundbuffer[bufferIndex]);
140 
141   /* Make sure current audio frame has not already been updated */
142   if (status & AUDIO_UPDATE)
143   {
144     /* Retrieve audio samples (size must be multiple of 32 bytes) */
145     bufferSize = audio_update(sb) * 4;
146     DCStoreRange((void *)sb, bufferSize);
147 
148     /* Mark current audio frame as being updated */
149     status &= ~AUDIO_UPDATE;
150   }
151 
152   /* Wait until previous audio frame is started before pushing current audio frame into DMA */
153   if ((status & AUDIO_WAIT) && !audioWait)
154   {
155     /* Update audio DMA settings for current frame */
156     AUDIO_InitDMA((u32)sb, bufferSize);
157 
158     /* Next soundbuffer */
159     bufferIndex = (bufferIndex + 1) % SOUND_BUFFER_NUM;
160 
161     /* Set audio wait flag */
162     audioWait = audioSync;
163 
164     /* Current audio frame is ready for upcoming DMA */
165     status &= ~AUDIO_WAIT;
166   }
167 
168   return status;
169 }
170 
171 /***
172       gx_audio_Start
173 
174      This function restarts the audio engine
175      This is called when coming back from Main Menu
176  ***/
gx_audio_Start(void)177 void gx_audio_Start(void)
178 {
179   /* shutdown background music */
180   PauseOgg(1);
181   StopOgg();
182 
183   /* shutdown menu audio processing */
184   ASND_Pause(1);
185   ASND_End();
186   AUDIO_StopDMA();
187   AUDIO_RegisterDMACallback(NULL);
188   DSP_Halt();
189 
190   /* DMA Interrupt callback */
191   AUDIO_RegisterDMACallback(ai_callback);
192 
193   /* emulation is synchronized with audio hardware by default */
194   audioSync = AUDIO_WAIT;
195 
196   /* reset emulation audio processing */
197   memset(soundbuffer, 0, sizeof(soundbuffer));
198   audioWait = 0;
199   bufferSize = 0;
200   bufferIndex = 0;
201 }
202 
203 /***
204       gx_audio_Stop
205 
206      This function stops current Audio DMA process
207      This is called when going back to Main Menu
208  ***/
gx_audio_Stop(void)209 void gx_audio_Stop(void)
210 {
211   /* restart menu audio processing */
212   DSP_Unhalt();
213   ASND_Init();
214   ASND_Pause(0);
215 
216   /* play background music */
217   if (Bg_music_ogg && !Shutdown)
218   {
219     PauseOgg(0);
220     PlayOgg((char *)Bg_music_ogg, Bg_music_ogg_size, 0, OGG_INFINITE_TIME);
221     SetVolumeOgg(((int)config.bgm_volume * 255) / 100);
222   }
223 }
224