Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / Core / HLE / sceMp3.cpp
blobf6ff940636723cf97e62c8d2234aef4209a6f63f
1 // Copyright (c) 2012- PPSSPP Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0 or later versions.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18 #include <map>
19 #include <algorithm>
21 #include "Core/Config.h"
22 #include "Core/HLE/HLE.h"
23 #include "Core/HLE/FunctionWrappers.h"
24 #include "Core/HLE/sceMp3.h"
25 #include "Core/HW/MediaEngine.h"
26 #include "Core/MemMap.h"
27 #include "Core/Reporting.h"
28 #include "Core/HW/SimpleAudioDec.h"
31 struct Mp3Context {
32 public:
34 int mp3StreamStart;
35 int mp3StreamEnd;
36 u32 mp3Buf;
37 int mp3BufSize;
38 u32 mp3PcmBuf;
39 int mp3PcmBufSize;
41 int readPosition;
43 int bufferRead;
44 int bufferWrite;
45 int bufferAvailable;
47 int mp3DecodedBytes;
48 int mp3LoopNum;
49 int mp3MaxSamples;
50 int mp3SumDecodedSamples;
52 int mp3Channels;
53 int mp3Bitrate;
54 int mp3SamplingRate;
55 int mp3Version;
57 void DoState(PointerWrap &p) {
58 auto s = p.Section("Mp3Context", 1);
59 if (!s)
60 return;
62 p.Do(mp3StreamStart);
63 p.Do(mp3StreamEnd);
64 p.Do(mp3Buf);
65 p.Do(mp3BufSize);
66 p.Do(mp3PcmBuf);
67 p.Do(mp3PcmBufSize);
68 p.Do(readPosition);
69 p.Do(bufferRead);
70 p.Do(bufferWrite);
71 p.Do(bufferAvailable);
72 p.Do(mp3DecodedBytes);
73 p.Do(mp3LoopNum);
74 p.Do(mp3MaxSamples);
75 p.Do(mp3SumDecodedSamples);
76 p.Do(mp3Channels);
77 p.Do(mp3Bitrate);
78 p.Do(mp3SamplingRate);
79 p.Do(mp3Version);
83 static std::map<u32, Mp3Context *> mp3Map_old;
84 static std::map<u32, AuCtx *> mp3Map;
85 static const int mp3DecodeDelay = 4000;
87 static AuCtx *getMp3Ctx(u32 mp3) {
88 if (mp3Map.find(mp3) == mp3Map.end())
89 return NULL;
90 return mp3Map[mp3];
93 void __Mp3Shutdown() {
94 for (auto it = mp3Map.begin(), end = mp3Map.end(); it != end; ++it) {
95 delete it->second;
97 mp3Map.clear();
100 void __Mp3DoState(PointerWrap &p) {
101 auto s = p.Section("sceMp3", 0, 2);
102 if (!s)
103 return;
105 if (s >= 2){
106 p.Do(mp3Map);
108 if (s <= 1 && p.mode == p.MODE_READ){
109 p.Do(mp3Map_old); // read old map
110 for (auto it = mp3Map_old.begin(), end = mp3Map_old.end(); it != end; ++it) {
111 auto mp3 = new AuCtx;
112 u32 id = it->first;
113 auto mp3_old = it->second;
114 mp3->AuBuf = mp3_old->mp3Buf;
115 mp3->AuBufSize = mp3_old->mp3BufSize;
116 mp3->PCMBuf = mp3_old->mp3PcmBuf;
117 mp3->PCMBufSize = mp3_old->mp3PcmBufSize;
118 mp3->BitRate = mp3_old->mp3Bitrate;
119 mp3->Channels = mp3_old->mp3Channels;
120 mp3->endPos = mp3_old->mp3StreamEnd;
121 mp3->startPos = mp3_old->mp3StreamStart;
122 mp3->LoopNum = mp3_old->mp3LoopNum;
123 mp3->SamplingRate = mp3_old->mp3SamplingRate;
124 mp3->freq = mp3->SamplingRate;
125 mp3->SumDecodedSamples = mp3_old->mp3SumDecodedSamples;
126 mp3->Version = mp3_old->mp3Version;
127 mp3->MaxOutputSample = mp3_old->mp3MaxSamples;
128 mp3->readPos = mp3_old->readPosition;
129 mp3->AuBufAvailable = 0; // reset to read from file
130 mp3->askedReadSize = 0;
131 mp3->realReadSize = 0;
133 mp3->audioType = PSP_CODEC_MP3;
134 mp3->decoder = new SimpleAudio(mp3->audioType);
135 mp3Map[id] = mp3;
140 static int sceMp3Decode(u32 mp3, u32 outPcmPtr) {
141 DEBUG_LOG(ME, "sceMp3Decode(%08x,%08x)", mp3, outPcmPtr);
143 AuCtx *ctx = getMp3Ctx(mp3);
144 if (!ctx) {
145 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
146 return -1;
149 int pcmBytes = ctx->AuDecode(outPcmPtr);
150 if (!pcmBytes) {
151 // decode data successfully, delay thread
152 hleDelayResult(pcmBytes, "mp3 decode", mp3DecodeDelay);
154 return pcmBytes;
157 static int sceMp3ResetPlayPosition(u32 mp3) {
158 DEBUG_LOG(ME, "SceMp3ResetPlayPosition(%08x)", mp3);
160 AuCtx *ctx = getMp3Ctx(mp3);
161 if (!ctx) {
162 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
163 return -1;
166 return ctx->AuResetPlayPosition();
169 static int sceMp3CheckStreamDataNeeded(u32 mp3) {
170 DEBUG_LOG(ME, "sceMp3CheckStreamDataNeeded(%08x)", mp3);
172 AuCtx *ctx = getMp3Ctx(mp3);
173 if (!ctx) {
174 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
175 return -1;
178 return ctx->AuCheckStreamDataNeeded();
181 static u32 sceMp3ReserveMp3Handle(u32 mp3Addr) {
182 INFO_LOG(ME, "sceMp3ReserveMp3Handle(%08x)", mp3Addr);
183 if (!Memory::IsValidAddress(mp3Addr)){
184 ERROR_LOG(ME, "sceMp3ReserveMp3Handle(%08x) invalid address %08x", mp3Addr, mp3Addr);
185 return -1;
188 AuCtx *Au = new AuCtx;
189 Au->startPos = Memory::Read_U64(mp3Addr); // Audio stream start position.
190 Au->endPos = Memory::Read_U32(mp3Addr + 8); // Audio stream end position.
191 Au->AuBuf = Memory::Read_U32(mp3Addr + 16); // Input Au data buffer.
192 Au->AuBufSize = Memory::Read_U32(mp3Addr + 20); // Input Au data buffer size.
193 Au->PCMBuf = Memory::Read_U32(mp3Addr + 24); // Output PCM data buffer.
194 Au->PCMBufSize = Memory::Read_U32(mp3Addr + 28); // Output PCM data buffer size.
196 DEBUG_LOG(ME, "startPos %llx endPos %llx mp3buf %08x mp3bufSize %08x PCMbuf %08x PCMbufSize %08x",
197 Au->startPos, Au->endPos, Au->AuBuf, Au->AuBufSize, Au->PCMBuf, Au->PCMBufSize);
199 Au->audioType = PSP_CODEC_MP3;
200 Au->Channels = 2;
201 Au->SumDecodedSamples = 0;
202 Au->MaxOutputSample = Au->PCMBufSize / 4;
203 Au->LoopNum = -1;
204 Au->AuBufAvailable = 0;
205 Au->readPos = Au->startPos;
207 // create Au decoder
208 Au->decoder = new SimpleAudio(Au->audioType);
210 // close the audio if mp3Addr already exist.
211 if (mp3Map.find(mp3Addr) != mp3Map.end()) {
212 delete mp3Map[mp3Addr];
213 mp3Map.erase(mp3Addr);
216 mp3Map[mp3Addr] = Au;
218 return mp3Addr;
221 static int sceMp3InitResource() {
222 WARN_LOG(ME, "UNIMPL: sceMp3InitResource");
223 // Do nothing here
224 return 0;
227 static int sceMp3TermResource() {
228 WARN_LOG(ME, "UNIMPL: sceMp3TermResource");
229 // Do nothing here
230 return 0;
233 static int __CalculateMp3Channels(int bitval) {
234 if (bitval == 0 || bitval == 1 || bitval == 2) { // Stereo / Joint Stereo / Dual Channel.
235 return 2;
237 else if (bitval == 3) { // Mono.
238 return 1;
240 else {
241 return -1;
245 static int __CalculateMp3SampleRates(int bitval, int mp3version) {
246 if (mp3version == 3) { // MPEG Version 1
247 int valuemapping[] = { 44100, 48000, 32000, -1 };
248 return valuemapping[bitval];
250 else if (mp3version == 2) { // MPEG Version 2
251 int valuemapping[] = { 22050, 24000, 16000, -1 };
252 return valuemapping[bitval];
254 else if (mp3version == 0) { // MPEG Version 2.5
255 int valuemapping[] = { 11025, 12000, 8000, -1 };
256 return valuemapping[bitval];
258 else {
259 return -1;
263 static int __CalculateMp3Bitrates(int bitval, int mp3version, int mp3layer) {
264 if (mp3version == 3) { // MPEG Version 1
265 if (mp3layer == 3) { // Layer I
266 int valuemapping[] = { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 };
267 return valuemapping[bitval];
269 else if (mp3layer == 2) { // Layer II
270 int valuemapping[] = { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 };
271 return valuemapping[bitval];
273 else if (mp3layer == 1) { // Layer III
274 int valuemapping[] = { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 };
275 return valuemapping[bitval];
277 else {
278 return -1;
281 else if (mp3version == 2 || mp3version == 0) { // MPEG Version 2 or 2.5
282 if (mp3layer == 3) { // Layer I
283 int valuemapping[] = { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 };
284 return valuemapping[bitval];
286 else if (mp3layer == 1 || mp3layer == 2) { // Layer II or III
287 int valuemapping[] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 };
288 return valuemapping[bitval];
290 else {
291 return -1;
294 else {
295 return -1;
299 static int __ParseMp3Header(AuCtx *ctx, bool *isID3) {
300 int header = bswap32(Memory::Read_U32(ctx->AuBuf));
301 // ID3 tag , can be seen in Hanayaka Nari Wa ga Ichizoku.
302 static const int ID3 = 0x49443300;
303 if ((header & 0xFFFFFF00) == ID3) {
304 *isID3 = true;
305 int size = bswap32(Memory::Read_U32(ctx->AuBuf + ctx->startPos + 6));
306 // Highest bit of each byte has to be ignored (format: 0x7F7F7F7F)
307 size = (size & 0x7F) | ((size & 0x7F00) >> 1) | ((size & 0x7F0000) >> 2) | ((size & 0x7F000000) >> 3);
308 header = bswap32(Memory::Read_U32(ctx->AuBuf + ctx->startPos + 10 + size));
310 return header;
313 static int sceMp3Init(u32 mp3) {
314 INFO_LOG(ME, "sceMp3Init(%08x)", mp3);
316 AuCtx *ctx = getMp3Ctx(mp3);
317 if (!ctx) {
318 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
319 return -1;
322 // Parse the Mp3 header
323 bool hasID3Tag = false;
324 int header = __ParseMp3Header(ctx, &hasID3Tag);
325 int layer = (header >> 17) & 0x3;
326 ctx->Version = ((header >> 19) & 0x3);
327 ctx->SamplingRate = __CalculateMp3SampleRates((header >> 10) & 0x3, ctx->Version);
328 ctx->Channels = __CalculateMp3Channels((header >> 6) & 0x3);
329 ctx->BitRate = __CalculateMp3Bitrates((header >> 12) & 0xF, ctx->Version, layer);
330 ctx->freq = ctx->SamplingRate;
332 INFO_LOG(ME, "sceMp3Init(): channels=%i, samplerate=%iHz, bitrate=%ikbps", ctx->Channels, ctx->SamplingRate, ctx->BitRate);
334 // for mp3, if required freq is 48000, reset resampling Frequency to 48000 seems get better sound quality (e.g. Miku Custom BGM)
335 if (ctx->freq == 48000) {
336 ctx->decoder->SetResampleFrequency(ctx->freq);
339 // For mp3 file, if ID3 tag is detected, we must move startPos to 0x400 (stream start position), remove 0x400 bytes of the sourcebuff, and reduce the available buffer size by 0x400
340 // this is very important for ID3 tag mp3, since our universal audio decoder is for decoding stream part only.
341 if (hasID3Tag) {
342 // if get ID3 tage, we will decode from 0x400
343 ctx->startPos = 0x400;
344 ctx->EatSourceBuff(0x400);
345 } else {
346 // if no ID3 tag, we will decode from the begining of the file
347 ctx->startPos = 0;
350 return 0;
353 static int sceMp3GetLoopNum(u32 mp3) {
354 DEBUG_LOG(ME, "sceMp3GetLoopNum(%08x)", mp3);
356 AuCtx *ctx = getMp3Ctx(mp3);
357 if (!ctx) {
358 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
359 return -1;
362 return ctx->AuGetLoopNum();
365 static int sceMp3GetMaxOutputSample(u32 mp3) {
366 DEBUG_LOG(ME, "sceMp3GetMaxOutputSample(%08x)", mp3);
367 AuCtx *ctx = getMp3Ctx(mp3);
368 if (!ctx) {
369 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
370 return -1;
373 return ctx->AuGetMaxOutputSample();
376 static int sceMp3GetSumDecodedSample(u32 mp3) {
377 INFO_LOG(ME, "sceMp3GetSumDecodedSample(%08X)", mp3);
379 AuCtx *ctx = getMp3Ctx(mp3);
380 if (!ctx) {
381 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
382 return -1;
385 return ctx->AuGetSumDecodedSample();
388 static int sceMp3SetLoopNum(u32 mp3, int loop) {
389 INFO_LOG(ME, "sceMp3SetLoopNum(%08X, %i)", mp3, loop);
391 AuCtx *ctx = getMp3Ctx(mp3);
392 if (!ctx) {
393 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
394 return -1;
397 return ctx->AuSetLoopNum(loop);
400 static int sceMp3GetMp3ChannelNum(u32 mp3) {
401 INFO_LOG(ME, "sceMp3GetMp3ChannelNum(%08X)", mp3);
403 AuCtx *ctx = getMp3Ctx(mp3);
404 if (!ctx) {
405 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
406 return -1;
409 return ctx->AuGetChannelNum();
412 static int sceMp3GetBitRate(u32 mp3) {
413 INFO_LOG(ME, "sceMp3GetBitRate(%08X)", mp3);
415 AuCtx *ctx = getMp3Ctx(mp3);
416 if (!ctx) {
417 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
418 return -1;
421 return ctx->AuGetBitRate();
424 static int sceMp3GetSamplingRate(u32 mp3) {
425 INFO_LOG(ME, "sceMp3GetSamplingRate(%08X)", mp3);
427 AuCtx *ctx = getMp3Ctx(mp3);
428 if (!ctx) {
429 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
430 return -1;
433 return ctx->AuGetSamplingRate();
436 static int sceMp3GetInfoToAddStreamData(u32 mp3, u32 dstPtr, u32 towritePtr, u32 srcposPtr) {
437 DEBUG_LOG(ME, "sceMp3GetInfoToAddStreamData(%08X, %08X, %08X, %08X)", mp3, dstPtr, towritePtr, srcposPtr);
439 AuCtx *ctx = getMp3Ctx(mp3);
440 if (!ctx) {
441 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
442 return -1;
445 return ctx->AuGetInfoToAddStreamData(dstPtr, towritePtr, srcposPtr);
448 static int sceMp3NotifyAddStreamData(u32 mp3, int size) {
449 DEBUG_LOG(ME, "sceMp3NotifyAddStreamData(%08X, %i)", mp3, size);
451 AuCtx *ctx = getMp3Ctx(mp3);
452 if (!ctx) {
453 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
454 return -1;
457 return ctx->AuNotifyAddStreamData(size);
460 static int sceMp3ReleaseMp3Handle(u32 mp3) {
461 INFO_LOG(ME, "sceMp3ReleaseMp3Handle(%08X)", mp3);
463 AuCtx *ctx = getMp3Ctx(mp3);
464 if (!ctx) {
465 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
466 return -1;
469 delete ctx;
470 mp3Map.erase(mp3);
472 return 0;
475 static u32 sceMp3EndEntry() {
476 ERROR_LOG_REPORT(ME, "UNIMPL sceMp3EndEntry(...)");
477 return 0;
480 static u32 sceMp3StartEntry() {
481 ERROR_LOG_REPORT(ME, "UNIMPL sceMp3StartEntry(...)");
482 return 0;
485 static u32 sceMp3GetFrameNum(u32 mp3) {
486 INFO_LOG(ME, "sceMp3GetFrameNum(%08x)", mp3);
487 AuCtx *ctx = getMp3Ctx(mp3);
488 if (!ctx) {
489 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
490 return -1;
492 return ctx->AuGetFrameNum();
495 static u32 sceMp3GetMPEGVersion(u32 mp3) {
496 INFO_LOG(ME, "sceMp3GetMPEGVersion(%08x)", mp3);
497 AuCtx *ctx = getMp3Ctx(mp3);
498 if (!ctx) {
499 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
500 return -1;
503 return ctx->AuGetVersion();
506 static u32 sceMp3ResetPlayPositionByFrame(u32 mp3, int position) {
507 DEBUG_LOG(ME, "sceMp3ResetPlayPositionByFrame(%08x, %i)", mp3, position);
508 AuCtx *ctx = getMp3Ctx(mp3);
509 if (!ctx) {
510 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
511 return -1;
514 return ctx->AuResetPlayPositionByFrame(position);
517 static u32 sceMp3LowLevelInit(u32 mp3) {
518 INFO_LOG(ME, "sceMp3LowLevelInit(%i)", mp3);
519 auto ctx = new AuCtx;
521 ctx->audioType = PSP_CODEC_MP3;
522 // create mp3 decoder
523 ctx->decoder = new SimpleAudio(ctx->audioType);
525 // close the audio if mp3 already exists.
526 if (mp3Map.find(mp3) != mp3Map.end()) {
527 delete mp3Map[mp3];
528 mp3Map.erase(mp3);
531 mp3Map[mp3] = ctx;
532 return 0;
535 static u32 sceMp3LowLevelDecode(u32 mp3, u32 sourceAddr, u32 sourceBytesConsumedAddr, u32 samplesAddr, u32 sampleBytesAddr) {
536 // sourceAddr: input mp3 stream buffer
537 // sourceBytesConsumedAddr: consumed bytes decoded in source
538 // samplesAddr: output pcm buffer
539 // sampleBytesAddr: output pcm size
540 DEBUG_LOG(ME, "sceMp3LowLevelDecode(%08x, %08x, %08x, %08x, %08x)", mp3, sourceAddr, sourceBytesConsumedAddr, samplesAddr, sampleBytesAddr);
542 AuCtx *ctx = getMp3Ctx(mp3);
543 if (!ctx) {
544 ERROR_LOG(ME, "%s: bad mp3 handle %08x", __FUNCTION__, mp3);
545 return -1;
548 if (!Memory::IsValidAddress(sourceAddr) || !Memory::IsValidAddress(sourceBytesConsumedAddr) ||
549 !Memory::IsValidAddress(samplesAddr) || !Memory::IsValidAddress(sampleBytesAddr)) {
550 ERROR_LOG(ME, "sceMp3LowLevelDecode(%08x, %08x, %08x, %08x, %08x) : invalid address in args", mp3, sourceAddr, sourceBytesConsumedAddr, samplesAddr, sampleBytesAddr);
551 return -1;
554 auto inbuff = Memory::GetPointer(sourceAddr);
555 auto outbuff = Memory::GetPointer(samplesAddr);
557 int outpcmbytes = 0;
558 ctx->decoder->Decode((void*)inbuff, 4096, outbuff, &outpcmbytes);
560 Memory::Write_U32(ctx->decoder->GetSourcePos(), sourceBytesConsumedAddr);
561 Memory::Write_U32(outpcmbytes, sampleBytesAddr);
562 return 0;
565 const HLEFunction sceMp3[] = {
566 {0X07EC321A, &WrapU_U<sceMp3ReserveMp3Handle>, "sceMp3ReserveMp3Handle", 'x', "x" },
567 {0X0DB149F4, &WrapI_UI<sceMp3NotifyAddStreamData>, "sceMp3NotifyAddStreamData", 'i', "xi" },
568 {0X2A368661, &WrapI_U<sceMp3ResetPlayPosition>, "sceMp3ResetPlayPosition", 'i', "x" },
569 {0X354D27EA, &WrapI_U<sceMp3GetSumDecodedSample>, "sceMp3GetSumDecodedSample", 'i', "x" },
570 {0X35750070, &WrapI_V<sceMp3InitResource>, "sceMp3InitResource", 'i', "" },
571 {0X3C2FA058, &WrapI_V<sceMp3TermResource>, "sceMp3TermResource", 'i', "" },
572 {0X3CEF484F, &WrapI_UI<sceMp3SetLoopNum>, "sceMp3SetLoopNum", 'i', "xi" },
573 {0X44E07129, &WrapI_U<sceMp3Init>, "sceMp3Init", 'i', "x" },
574 {0X732B042A, &WrapU_V<sceMp3EndEntry>, "sceMp3EndEntry", 'x', "" },
575 {0X7F696782, &WrapI_U<sceMp3GetMp3ChannelNum>, "sceMp3GetMp3ChannelNum", 'i', "x" },
576 {0X87677E40, &WrapI_U<sceMp3GetBitRate>, "sceMp3GetBitRate", 'i', "x" },
577 {0X87C263D1, &WrapI_U<sceMp3GetMaxOutputSample>, "sceMp3GetMaxOutputSample", 'i', "x" },
578 {0X8AB81558, &WrapU_V<sceMp3StartEntry>, "sceMp3StartEntry", 'x', "" },
579 {0X8F450998, &WrapI_U<sceMp3GetSamplingRate>, "sceMp3GetSamplingRate", 'i', "x" },
580 {0XA703FE0F, &WrapI_UUUU<sceMp3GetInfoToAddStreamData>, "sceMp3GetInfoToAddStreamData", 'i', "xxxx" },
581 {0XD021C0FB, &WrapI_UU<sceMp3Decode>, "sceMp3Decode", 'i', "xx" },
582 {0XD0A56296, &WrapI_U<sceMp3CheckStreamDataNeeded>, "sceMp3CheckStreamDataNeeded", 'i', "x" },
583 {0XD8F54A51, &WrapI_U<sceMp3GetLoopNum>, "sceMp3GetLoopNum", 'i', "x" },
584 {0XF5478233, &WrapI_U<sceMp3ReleaseMp3Handle>, "sceMp3ReleaseMp3Handle", 'i', "x" },
585 {0XAE6D2027, &WrapU_U<sceMp3GetMPEGVersion>, "sceMp3GetMPEGVersion", 'x', "x" },
586 {0X3548AEC8, &WrapU_U<sceMp3GetFrameNum>, "sceMp3GetFrameNum", 'x', "x" },
587 {0X0840E808, &WrapU_UI<sceMp3ResetPlayPositionByFrame>, "sceMp3ResetPlayPositionByFrame", 'x', "xi" },
588 {0X1B839B83, &WrapU_U<sceMp3LowLevelInit>, "sceMp3LowLevelInit", 'x', "x" },
589 {0XE3EE2C81, &WrapU_UUUUU<sceMp3LowLevelDecode>, "sceMp3LowLevelDecode", 'x', "xxxxx"}
592 void Register_sceMp3() {
593 RegisterModule("sceMp3", ARRAY_SIZE(sceMp3), sceMp3);