1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - libraries - snd - common
3   File:     snd_bank.c
4 
5   Copyright 2004-2008 Nintendo. All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2008-09-18#$
14   $Rev: 8573 $
15   $Author: okubata_ryoma $
16  *---------------------------------------------------------------------------*/
17 
18 #include <nitro/snd/common/bank.h>
19 
20 #ifndef SDK_FROM_TOOL
21 
22 #include <nitro/misc.h>
23 #include <nitro/mi.h>
24 #include <nitro/snd/common/main.h>
25 
26 #else
27 
28 #define SDK_ASSERT(exp)           ((void) 0)
29 #define SDK_NULL_ASSERT(exp)           ((void) 0)
30 #define SDK_MINMAX_ASSERT(exp, min, max)           ((void) 0)
31 
32 #define SNDi_LockMutex() ((void) 0)
33 #define SNDi_UnlockMutex() ((void) 0)
34 
MI_ReadByte(const void * address)35 static u8 MI_ReadByte(const void *address)
36 {
37     return *(u8 *)address;
38 }
39 
40 #define HW_MAIN_MEM             0x02000000
41 
42 #ifdef _MSC_VER
43 #pragma warning( disable : 4018 )      // Warning: Signed/unsigned mismatch
44 #pragma warning( disable : 4311 )      // Warning: Pointer truncation from 'type' to 'type'
45 #pragma warning( disable : 4312 )      // Warning: Conversion from 'type' to 'type' of greater size
46 #endif
47 
48 #endif /* SDK_FROM_TOOL */
49 
50 /******************************************************************************
51 	Static function declarations
52  ******************************************************************************/
53 static const SNDWaveData *GetWaveData(const SNDBankData *bank, int waveArcNo, int waveIndex);
54 
55 /******************************************************************************
56 	Public functions
57  ******************************************************************************/
58 
59 /*---------------------------------------------------------------------------*
60   Name:         SND_AssignWaveArc
61 
62   Description:  Assigns a waveform archive to bank (ThreadSafe).
63 
64   Arguments:    bank    - Pointer to SNDBankData structure
65                 index   - Wave archive index
66                 waveArc - Pointer to SNDWaveArc structure
67 
68   Returns:      None.
69  *---------------------------------------------------------------------------*/
SND_AssignWaveArc(SNDBankData * bank,int index,SNDWaveArc * waveArc)70 void SND_AssignWaveArc(SNDBankData *bank, int index, SNDWaveArc *waveArc)
71 {
72     SNDWaveArcLink *next;
73     SNDWaveArcLink *prev;
74 
75     SDK_NULL_ASSERT(bank);
76     SDK_NULL_ASSERT(waveArc);
77 #ifdef SDK_ARM9
78     SDK_ASSERTMSG(((u32)bank & 0x1f) == 0, "bank address must be aligned 32 bytes boundary.");
79     SDK_ASSERTMSG(((u32)waveArc & 0x1f) == 0, "waveArc address must be aligned 32 bytes boundary.");
80 #endif
81     SDK_MINMAX_ASSERT(index, 0, SND_BANK_TO_WAVEARC_MAX - 1);
82 
83     SNDi_LockMutex();
84 
85     if (bank->waveArcLink[index].waveArc != NULL)
86     {
87         // When already connected
88 
89         // If it's the same as the waveform archive to which it is trying to connect, it does nothing
90         if (waveArc == bank->waveArcLink[index].waveArc)
91         {
92             SNDi_UnlockMutex();
93             return;
94         }
95 
96         // If a different waveform archive is connected, it disconnects once
97         if (&bank->waveArcLink[index] == bank->waveArcLink[index].waveArc->topLink)
98         {
99             bank->waveArcLink[index].waveArc->topLink = bank->waveArcLink[index].next;
100 
101 #ifdef SDK_ARM9
102             DC_StoreRange(bank->waveArcLink[index].waveArc, sizeof(SNDWaveArc));
103 #endif
104         }
105         else
106         {
107             prev = bank->waveArcLink[index].waveArc->topLink;
108             while (prev != NULL)
109             {
110                 if (&bank->waveArcLink[index] == prev->next)
111                     break;
112                 prev = prev->next;
113             }
114             SDK_NULL_ASSERT(prev);
115             prev->next = bank->waveArcLink[index].next;
116 
117 #ifdef SDK_ARM9
118             DC_StoreRange(prev, sizeof(SNDWaveArcLink));
119 #endif
120         }
121     }
122 
123     next = waveArc->topLink;
124     waveArc->topLink = &bank->waveArcLink[index];
125     bank->waveArcLink[index].next = next;
126     bank->waveArcLink[index].waveArc = waveArc;
127 
128     SNDi_UnlockMutex();
129 
130 #ifdef SDK_ARM9
131     DC_StoreRange(bank, sizeof(SNDBankData));
132     DC_StoreRange(waveArc, sizeof(SNDWaveArc));
133 #endif
134 }
135 
136 /*---------------------------------------------------------------------------*
137   Name:         SND_DestroyBank
138 
139   Description:  Destroys bank (ThreadSafe).
140 
141   Arguments:    bank    - Pointer to SNDBankData structure
142 
143   Returns:      None.
144  *---------------------------------------------------------------------------*/
SND_DestroyBank(SNDBankData * bank)145 void SND_DestroyBank(SNDBankData *bank)
146 {
147     SNDWaveArc *waveArc;
148     SNDWaveArcLink *prev;
149     int     i;
150 
151     SDK_NULL_ASSERT(bank);
152 #ifdef SDK_ARM9
153     SDK_ASSERTMSG(((u32)bank & 0x1f) == 0, "bank address must be aligned 32 bytes boundary.");
154 #endif
155 
156     SNDi_LockMutex();
157 
158     for (i = 0; i < SND_BANK_TO_WAVEARC_MAX; i++)
159     {
160         waveArc = bank->waveArcLink[i].waveArc;
161         if (waveArc == NULL)
162             continue;
163 
164         if (&bank->waveArcLink[i] == waveArc->topLink)
165         {
166             waveArc->topLink = bank->waveArcLink[i].next;
167 
168 #ifdef SDK_ARM9
169             DC_StoreRange(waveArc, sizeof(SNDWaveArc));
170 #endif
171         }
172         else
173         {
174             prev = waveArc->topLink;
175             while (prev != NULL)
176             {
177                 if (&bank->waveArcLink[i] == prev->next)
178                     break;
179                 prev = prev->next;
180             }
181             SDK_NULL_ASSERT(prev);
182             prev->next = bank->waveArcLink[i].next;
183 
184 #ifdef SDK_ARM9
185             DC_StoreRange(prev, sizeof(SNDWaveArcLink));
186 #endif
187         }
188     }
189 
190     SNDi_UnlockMutex();
191 }
192 
193 /*---------------------------------------------------------------------------*
194   Name:         SND_DestroyWaveArc
195 
196   Description:  Destroys the waveform archive (ThreadSafe).
197 
198   Arguments:    waveArc - Pointer to SNDWaveArc structure
199 
200   Returns:      None.
201  *---------------------------------------------------------------------------*/
SND_DestroyWaveArc(SNDWaveArc * waveArc)202 void SND_DestroyWaveArc(SNDWaveArc *waveArc)
203 {
204     SNDWaveArcLink *link;
205     SNDWaveArcLink *next;
206 
207     SDK_NULL_ASSERT(waveArc);
208 #ifdef SDK_ARM9
209     SDK_ASSERTMSG(((u32)waveArc & 0x1f) == 0, "waveArc address must be aligned 32 bytes boundary.");
210 #endif
211 
212     SNDi_LockMutex();
213 
214     link = waveArc->topLink;
215     while (link != NULL)
216     {
217         next = link->next;
218 
219         link->waveArc = NULL;
220         link->next = NULL;
221 
222 #ifdef SDK_ARM9
223         DC_StoreRange(link, sizeof(SNDWaveArcLink));
224 #endif
225         link = next;
226     }
227 
228     SNDi_UnlockMutex();
229 }
230 
231 
232 /*---------------------------------------------------------------------------*
233   Name:         SND_ReadInstData
234 
235   Description:  Gets instrument parameters from bank (ThreadSafe).
236 
237   Arguments:    bank    - Pointer to SNDBankData structure
238                 prgNo - Program number
239                 key - Note key
240                 inst - Pointer to SNDInstData structure
241 
242   Returns:      Whether it was successful.
243  *---------------------------------------------------------------------------*/
SND_ReadInstData(const SNDBankData * bank,int prgNo,int key,SNDInstData * inst)244 BOOL SND_ReadInstData(const SNDBankData *bank, int prgNo, int key, SNDInstData *inst)
245 {
246     u32     instOffset;
247 
248     SDK_NULL_ASSERT(bank);
249     SDK_NULL_ASSERT(inst);
250 
251     if (prgNo < 0)
252         return FALSE;
253 
254 #ifdef SDK_FROM_TOOL
255     if (bank->fileHeader.signature[0] == 'S' &&
256         bank->fileHeader.signature[1] == 'B' &&
257         bank->fileHeader.signature[2] == 'C' && bank->fileHeader.signature[3] == 'B')
258     {
259         const SNDBankDataCallback *bankcb = (const SNDBankDataCallback *)bank;
260         return bankcb->readInstDataFunc(bankcb, prgNo, key, inst);
261     }
262 #endif
263 
264     SNDi_LockMutex();
265 
266     if (prgNo >= bank->instCount)
267     {                                  // Note: Read from MainMemory
268         SNDi_UnlockMutex();
269         return FALSE;
270     }
271 
272     instOffset = bank->instOffset[prgNo];       // Note: Read from MainMemory
273     inst->type = (u8)(instOffset & 0xff);
274     instOffset >>= 8;
275 
276     // Process for each instrument type
277     switch (inst->type)
278     {
279     case SND_INST_PCM:
280     case SND_INST_PSG:
281     case SND_INST_NOISE:
282     case SND_INST_DIRECTPCM:
283     case SND_INST_NULL:
284         {
285             const SNDInstParam *param = (const SNDInstParam *)((u8 *)bank + instOffset);
286 
287             inst->param = *param;      // Structure copy
288             break;
289         }
290 
291     case SND_INST_DRUM_SET:
292         {
293             const SNDDrumSet *drumSet = (const SNDDrumSet *)((u8 *)bank + instOffset);
294             u8      min = MI_ReadByte(&drumSet->min);
295             u8      max = MI_ReadByte(&drumSet->max);
296 
297             if (key < min || key > max)
298             {
299                 SNDi_UnlockMutex();
300                 return FALSE;
301             }
302 
303             *inst = drumSet->instOffset[key - min];     // Structure copy
304             break;
305         }
306 
307     case SND_INST_KEY_SPLIT:
308         {
309             int     index = 0;
310             const SNDKeySplit *keySplit = (const SNDKeySplit *)((u8 *)bank + instOffset);
311 
312             while (key > MI_ReadByte(&keySplit->key[index]))
313             {
314                 index++;
315                 if (index >= SND_INST_KEYSPLIT_MAX)
316                 {
317                     SNDi_UnlockMutex();
318                     return FALSE;
319                 }
320             }
321 
322             *inst = keySplit->instOffset[index];        // Structure copy
323             break;
324         }
325 
326     case SND_INST_INVALID:
327     default:
328         SNDi_UnlockMutex();
329         return FALSE;
330     }
331 
332     SNDi_UnlockMutex();
333 
334     return TRUE;
335 }
336 
337 /*---------------------------------------------------------------------------*
338   Name:         SND_WriteInstData
339 
340   Description:  Writes instrument data (ThreadSafe).
341 
342   Arguments:    bank    - Pointer to SNDBankData structure
343                 prgNo - Program number
344                 key - Note key
345                 inst - Pointer to SNDInstData structure
346 
347   Returns:      Whether it was successful.
348  *---------------------------------------------------------------------------*/
SND_WriteInstData(SNDBankData * bank,int prgNo,int key,const struct SNDInstData * inst)349 BOOL SND_WriteInstData(SNDBankData *bank, int prgNo, int key, const struct SNDInstData *inst)
350 {
351     u32     instOffset;
352     u8      type;
353 
354     SDK_NULL_ASSERT(bank);
355     SDK_NULL_ASSERT(inst);
356 
357     if (prgNo < 0)
358         return FALSE;
359 
360     SNDi_LockMutex();
361     if (prgNo >= bank->instCount)
362     {                                  // Note: Read from MainMemory
363         SNDi_UnlockMutex();
364         return FALSE;
365     }
366 
367     instOffset = bank->instOffset[prgNo];       // Note: Read from MainMemory
368     type = (u8)(instOffset & 0xff);
369     instOffset >>= 8;
370 
371     // Process for each instrument type
372     switch (type)
373     {
374     case SND_INST_PCM:
375     case SND_INST_PSG:
376     case SND_INST_NOISE:
377     case SND_INST_DIRECTPCM:
378     case SND_INST_NULL:
379         {
380             SNDInstParam *param = (SNDInstParam *)((u8 *)bank + instOffset);
381 
382             bank->instOffset[prgNo] = (instOffset << 8) | inst->type;
383             *param = inst->param;      // Structure copy
384             break;
385         }
386 
387     case SND_INST_DRUM_SET:
388         {
389             SNDDrumSet *drumSet = (SNDDrumSet *)((u8 *)bank + instOffset);
390             u8      min = MI_ReadByte(&drumSet->min);
391             u8      max = MI_ReadByte(&drumSet->max);
392 
393             if (key < min || key > max)
394             {
395                 SNDi_UnlockMutex();
396                 return FALSE;
397             }
398 
399             drumSet->instOffset[key - min] = *inst;     // Structure copy
400             break;
401         }
402 
403     case SND_INST_KEY_SPLIT:
404         {
405             int     index = 0;
406             SNDKeySplit *keySplit = (SNDKeySplit *)((u8 *)bank + instOffset);
407 
408             while (key > MI_ReadByte(&keySplit->key[index]))
409             {
410                 index++;
411                 if (index >= SND_INST_KEYSPLIT_MAX)
412                 {
413                     SNDi_UnlockMutex();
414                     return FALSE;
415                 }
416             }
417 
418             keySplit->instOffset[index] = *inst;        // Structure copy
419             break;
420         }
421 
422     case SND_INST_INVALID:
423     default:
424         SNDi_UnlockMutex();
425         return FALSE;
426     }
427 
428     SNDi_UnlockMutex();
429 
430 #ifdef SDK_ARM9
431     DC_StoreRange(bank, bank->fileHeader.fileSize);
432 #endif
433 
434     return TRUE;
435 }
436 
437 /*---------------------------------------------------------------------------*
438   Name:         SND_GetFirstInstDataPos
439 
440   Description:  Gets SNDInstPos for instrument data scanning.
441 
442   Arguments:    bank - Pointer to bank data
443 
444   Returns:      SNDInstPos structure.
445  *---------------------------------------------------------------------------*/
SND_GetFirstInstDataPos(const SNDBankData * bank)446 SNDInstPos SND_GetFirstInstDataPos(const SNDBankData *bank)
447 {
448     SNDInstPos pos;
449 
450     SDK_NULL_ASSERT(bank);
451 
452     (void)bank;
453 
454     pos.prgNo = 0;
455     pos.index = 0;
456 
457     return pos;
458 }
459 
460 /*---------------------------------------------------------------------------*
461   Name:         SND_GetNextInstData
462 
463   Description:  Gets the next instrument data.
464 
465   Arguments:    bank - Pointer to bank data
466                 inst - Pointer that stores the obtained instrument data
467                 pos - Pointer to the SNDInstPos structure
468 
469   Returns:      Whether successfully obtained.
470  *---------------------------------------------------------------------------*/
SND_GetNextInstData(const SNDBankData * bank,SNDInstData * inst,SNDInstPos * pos)471 BOOL SND_GetNextInstData(const SNDBankData *bank, SNDInstData *inst, SNDInstPos *pos)
472 {
473     SDK_NULL_ASSERT(bank);
474     SDK_NULL_ASSERT(inst);
475     SDK_NULL_ASSERT(pos);
476 
477     for (; pos->prgNo < bank->instCount; pos->prgNo++, pos->index = 0)
478     {
479         u32     instOffset = bank->instOffset[pos->prgNo];
480         inst->type = (u8)(instOffset & 0xff);
481         instOffset >>= 8;
482 
483         switch (inst->type)
484         {
485         case SND_INST_PCM:
486         case SND_INST_PSG:
487         case SND_INST_NOISE:
488         case SND_INST_DIRECTPCM:
489         case SND_INST_NULL:
490             {
491                 const SNDInstParam *param = (const SNDInstParam *)((u8 *)bank + instOffset);
492                 inst->param = *param;
493                 pos->prgNo++;
494                 return TRUE;
495             }
496 
497         case SND_INST_DRUM_SET:
498             {
499                 const SNDDrumSet *drumSet = (const SNDDrumSet *)((u8 *)bank + instOffset);
500                 for (; pos->index < drumSet->max - drumSet->min + 1; pos->index++)
501                 {
502                     *inst = drumSet->instOffset[pos->index];
503                     pos->index++;
504                     return TRUE;
505                 }
506                 break;
507             }
508 
509         case SND_INST_KEY_SPLIT:
510             {
511                 const SNDKeySplit *keySplit = (const SNDKeySplit *)((u8 *)bank + instOffset);
512                 for (; pos->index < SND_INST_KEYSPLIT_MAX; pos->index++)
513                 {
514                     if (keySplit->key[pos->index] == 0)
515                         break;
516                     *inst = keySplit->instOffset[pos->index];
517                     pos->index++;
518                     return TRUE;
519                 }
520                 break;
521             }
522 
523         default:
524             break;
525         }
526     }
527 
528     return FALSE;
529 }
530 
531 /*---------------------------------------------------------------------------*
532   Name:         SND_GetWaveDataCount
533 
534   Description:  Gets the number of waveform data in the waveform archive.
535 
536   Arguments:    waveArc - Pointer to the waveform archive
537 
538   Returns:      Number of waveform data.
539  *---------------------------------------------------------------------------*/
SND_GetWaveDataCount(const struct SNDWaveArc * waveArc)540 u32 SND_GetWaveDataCount(const struct SNDWaveArc *waveArc)
541 {
542     SDK_NULL_ASSERT(waveArc);
543 
544     return waveArc->waveCount;
545 }
546 
547 /*---------------------------------------------------------------------------*
548   Name:         SND_SetWaveDataAddress
549 
550   Description:  Registers waveform data to the waveform archive.
551 
552   Arguments:    waveArc - Pointer to the waveform archive
553                 index - Index number of waveform data
554                 address - Address of waveform data
555 
556   Returns:      None.
557  *---------------------------------------------------------------------------*/
SND_SetWaveDataAddress(SNDWaveArc * waveArc,int index,const SNDWaveData * address)558 void SND_SetWaveDataAddress(SNDWaveArc *waveArc, int index, const SNDWaveData *address)
559 {
560     SDK_NULL_ASSERT(waveArc);
561     SDK_MINMAX_ASSERT(index, 0, waveArc->waveCount);
562     SDK_ASSERT(address == NULL || address >= (void *)HW_MAIN_MEM);
563 
564     SNDi_LockMutex();
565 
566     waveArc->waveOffset[index] = (u32)address;
567 
568 #ifdef SDK_ARM9
569     DC_StoreRange(&waveArc->waveOffset[index], sizeof(u32));
570 #endif
571 
572     SNDi_UnlockMutex();
573 }
574 
575 /*---------------------------------------------------------------------------*
576   Name:         SND_GetWaveDataAddress
577 
578   Description:  Gets the waveform data registered in the waveform archive.
579 
580   Arguments:    waveArc - Pointer to the waveform archive
581                 index - Index number of waveform data
582 
583   Returns:      Pointer to the obtained waveform data.
584  *---------------------------------------------------------------------------*/
SND_GetWaveDataAddress(const SNDWaveArc * waveArc,int index)585 const SNDWaveData *SND_GetWaveDataAddress(const SNDWaveArc *waveArc, int index)
586 {
587     const SNDWaveData *wave;
588     u32     offset;
589 
590     SDK_NULL_ASSERT(waveArc);
591     SDK_MINMAX_ASSERT(index, 0, waveArc->waveCount);
592 
593     SNDi_LockMutex();
594 
595     offset = waveArc->waveOffset[index];
596 
597     if (offset != 0)
598     {
599         if (offset < HW_MAIN_MEM)
600             wave = (const SNDWaveData *)((u8 *)waveArc + offset);
601         else
602             wave = (const SNDWaveData *)offset;
603     }
604     else
605     {
606         wave = NULL;
607     }
608 
609     SNDi_UnlockMutex();
610 
611     return wave;
612 }
613 
614 //-----------------------------------------------------------------------------
615 // ARM7 only
616 
617 #ifdef SDK_ARM7
618 
619 /*---------------------------------------------------------------------------*
620   Name:         SND_NoteOn
621 
622   Description:  Performs note-on using the bank.
623 
624   Arguments:
625 
626   Returns:      Pointer to SNDChannel structure.
627  *---------------------------------------------------------------------------*/
SND_NoteOn(SNDExChannel * ch_p,int key,int velocity,s32 length,const SNDBankData * bank,const SNDInstData * inst)628 BOOL SND_NoteOn(SNDExChannel *ch_p,
629                 int key, int velocity, s32 length, const SNDBankData *bank, const SNDInstData *inst)
630 {
631     const SNDWaveData *wave_data;
632     int     release;
633     BOOL    result;
634 
635     SDK_NULL_ASSERT(ch_p);
636     SDK_MINMAX_ASSERT(key, 0, 127);
637     SDK_MINMAX_ASSERT(velocity, 0, 127);
638     SDK_NULL_ASSERT(inst);
639 
640     release = inst->param.release;
641     if (inst->param.release == SND_BANK_DISABLE_RELEASE)
642     {
643         length = -1;
644         release = 0;
645     }
646 
647     switch (inst->type)
648     {
649     case SND_INST_PCM:
650     case SND_INST_DIRECTPCM:
651         if (inst->type == SND_INST_PCM)
652         {
653             wave_data = GetWaveData(bank, inst->param.wave[1], inst->param.wave[0]);
654         }
655         else
656         {
657             wave_data = (const SNDWaveData *)(inst->param.wave[1] << 16 | inst->param.wave[0]);
658         }
659 
660         if (wave_data != NULL)
661         {
662             result = SND_StartExChannelPcm(ch_p, &wave_data->param, wave_data->samples, length);
663         }
664         else
665         {
666             result = FALSE;
667         }
668         break;
669 
670     case SND_INST_PSG:
671         result = SND_StartExChannelPsg(ch_p, (SNDDuty)inst->param.wave[0], length);
672         break;
673 
674     case SND_INST_NOISE:
675         result = SND_StartExChannelNoise(ch_p, length);
676         break;
677 
678     default:
679         result = FALSE;
680         break;
681     }
682 
683     if (!result)
684         return FALSE;
685 
686     ch_p->key = (u8)key;
687     ch_p->original_key = inst->param.original_key;
688     ch_p->velocity = (u8)velocity;
689 
690     SND_SetExChannelAttack(ch_p, inst->param.attack);
691     SND_SetExChannelDecay(ch_p, inst->param.decay);
692     SND_SetExChannelSustain(ch_p, inst->param.sustain);
693     SND_SetExChannelRelease(ch_p, release);
694 
695     ch_p->init_pan = (s8)(inst->param.pan - 64);
696 
697     return TRUE;
698 }
699 
700 #endif
701 
702 /******************************************************************************
703 	Static Functions
704  ******************************************************************************/
705 
706 //-----------------------------------------------------------------------------
707 // ARM7 only
708 
709 #ifdef SDK_ARM7
710 
711 /*---------------------------------------------------------------------------*
712   Name:         GetWaveData
713 
714   Description:  Gets waveform data from the waveform archive.
715 
716   Arguments:    bank - Pointer to SNDBankData
717                 waveArcNo - Wave archive number
718                 waveIndex - Wave index
719 
720   Returns:      Pointer to SNDWaveData structure.
721  *---------------------------------------------------------------------------*/
GetWaveData(const SNDBankData * bank,int waveArcNo,int waveIndex)722 static const SNDWaveData *GetWaveData(const SNDBankData *bank, int waveArcNo, int waveIndex)
723 {
724     const SNDWaveArc *arc;
725 
726     SDK_MINMAX_ASSERT(waveArcNo, 0, SND_BANK_TO_WAVEARC_MAX - 1);
727 
728     arc = bank->waveArcLink[waveArcNo].waveArc;
729 
730     if (arc == NULL)
731         return NULL;
732     if (waveIndex >= arc->waveCount)
733         return NULL;
734 
735     return SND_GetWaveDataAddress(arc, waveIndex);
736 }
737 
738 #endif
739 
740 /*====== End of snd_bank.c ======*/
741