2 * FryingPan - Amiga CD/DVD Recording Software (User Interface and supporting Libraries only)
3 * Copyright (C) 2001-2011 Tomasz Wiszkowski Tomasz.Wiszkowski at gmail.com
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "rMP3Audio.h"
21 #include <libclass/dos.h>
22 #include <libclass/exec.h>
23 #include <libclass/utility.h>
24 #include <LibC/LibC.h>
26 #define FLIP16(a) ((((a)&0xff00)>>8) | (((a)&0xff)<<8))
27 #define MKID(a,b,c,d) (((a&255)<<24)|((b&255)<<16)|((c&255)<<8)|(d&255))
28 #define ID_ID3 MKID('I','D','3','\000')
30 bool rMP3Audio::sNoMpegaReported
= false;
32 IFileReader
*rMP3Audio::openRead(const char* sFile
, EDtError
&rc
)
35 if (true == checkFile(sFile
, rc
))
37 pSkel
= new rMP3Audio(sFile
, rc
);
42 rMP3Audio::rMP3Audio(const char *sName
, EDtError
&rc
)
43 : FileReader(sName
, rc
)
48 // set pStream to null == we do not have anything yet
51 hAccess
.Initialize(this, &rMP3Audio::access
);
53 for (int i
=0; i
<MPEGA_MAX_CHANNELS
; i
++)
55 pPCM
[i
] = new uint16
[MPEGA_PCM_SIZE
];
58 pMPEGA
= MPEGAIFace::GetInstance(MPEGA_VERSION
);
59 _D(Lvl_Info
, "%s: MPEGA Interface at %lx",
60 (int)__PRETTY_FUNCTION__
,
65 rc
= DT_RequiredResourceNotAvailable
;
69 // initialize mpega_ctrl
70 fillControl(&hControl
);
73 setLittleEndian(false);
77 handle
= DOS
->Open((char*)getFileName(), MODE_OLDFILE
);
82 rMP3Audio::~rMP3Audio()
84 for (int i
=0; i
<MPEGA_MAX_CHANNELS
; i
++)
95 pMPEGA
->FreeInstance();
96 _D(Lvl_Info
, "%s: MPEGA Interface disposed", (int)__PRETTY_FUNCTION__
);
101 void rMP3Audio::readHeaders(EDtError
&rc
)
103 MPEGA_STREAM
*pStream
;
106 * FIRST OF ALL determine the correct beginning and end of stream
107 * since MPEGA is freaking unable to do that.
113 stream_size
= 0; // we will modify this value slightly...
115 // 1. check for tag at the end
116 DOS
->Seek(handle
, -128, OFFSET_END
);
117 DOS
->Read(handle
, &x
, 128);
118 if ((x
[0]== 'T') && (x
[1] == 'A') && (x
[2] == 'G'))
121 _D(Lvl_Info
, "%s: found ID3v1 tag. Structure size: 128 bytes", (uint
)__PRETTY_FUNCTION__
);
123 else if ((x
[118] == '3') && (x
[119] == 'D') && (x
[120] == 'I'))
126 _D(Lvl_Info
, "%s: found ID3v2.%ld.%ld footer. Structure size: 10 bytes", (uint
)__PRETTY_FUNCTION__
, x
[121], x
[122]);
129 // 2. get file length (now we can)
130 sz
= DOS
->Seek(handle
, 0, OFFSET_BEGINNING
);
133 // 3. check for id3v2
134 DOS
->Read(handle
, &x
, 10);
135 if ((x
[0] == 'I') && (x
[1] == 'D') && (x
[2] == '3'))
137 sz
= (x
[6]<<21) | (x
[7]<<14) | (x
[8]<<7) | (x
[9]);
138 stream_start
= sz
+10;
139 stream_size
-= stream_start
;
140 _D(Lvl_Info
, "%s: found ID3v2.%ld.%ld tag. Structure size: %ld bytes", (uint
)__PRETTY_FUNCTION__
, x
[3], x
[4], sz
);
144 // 4. set remaining fields
145 current_location
= 0;
148 pStream
= pMPEGA
->MPEGA_open(getFileName(), &hControl
);
149 ASSERTS(NULL
!= pStream
, "Verify routine passed stream that does not open!\nPlease do NOT use this track.");
153 _D(Lvl_Info
, "%s: stream %08lx opened, len: %ld ms br: %ld, fr: %ld, ch: %ld",
154 (int)__PRETTY_FUNCTION__
,
156 pStream
->ms_duration
,
161 unsigned long lLen
= 0;
163 pMPEGA
->MPEGA_seek(pStream
, pStream
->ms_duration
);
164 pMPEGA
->MPEGA_time(pStream
, &lLen
);
165 pMPEGA
->MPEGA_seek(pStream
, 0);
167 _D(Lvl_Info
, "%s: MPEGA claims this file is %ldms long. This file turns out to be %ldms",
168 (int)__PRETTY_FUNCTION__
,
169 pStream
->ms_duration
,
172 //lLen = pStream->ms_duration;
173 lLen
*= 1764; // we need 44100Hz, *2 (stereo), *2 (16bit), that is 176400 bytes per second
175 lLen
/= getBlockSize(); //
176 lLen
*= getBlockSize(); // round down to nearest multiplicity of block size
180 pMPEGA
->MPEGA_close(pStream
);
185 bool rMP3Audio::checkFile(const char* sFileName
, EDtError
&rc
)
191 // 1. verify we have the library.
192 // DPrintfA("Accessing mpega...\n");
194 rc
= DT_RequiredResourceNotAvailable
;
196 pBase
= Exec
->OpenLibrary("mpega.library", MPEGA_VERSION
);
198 if ((false == sNoMpegaReported
) && (NULL
== pBase
))
200 request("Information",
201 "MPEGA library seems to be missing.\nAs a result, your MP3 files will not work.\n\nThis message will not pop up any more.",
204 sNoMpegaReported
= true;
208 ASSERT(pBase
!= NULL
);
209 Exec
->CloseLibrary(pBase
);
212 // 2. verify the file is EXACTLY what we are looking for.
213 // DPrintfA("accessing file...\n");
215 rc
= DT_UnableToOpenFile
;
217 fh
= DOS
->Open(const_cast<char*>(sFileName
), MODE_OLDFILE
);
221 // DPrintfA("checking file...\n");
222 bRes
= checkMP3(fh
, rc
);
227 void rMP3Audio::dispose()
232 const char *rMP3Audio::static_getName()
234 return "MP3 Audio Track";
237 bool rMP3Audio::static_isAudio()
242 bool rMP3Audio::static_isSession()
247 bool rMP3Audio::static_isData()
252 bool rMP3Audio::isAudio()
254 return static_isAudio();
257 bool rMP3Audio::isData()
259 return static_isData();
262 const char *rMP3Audio::getName()
264 return static_getName();
267 void rMP3Audio::fillControl(MPEGA_CTRL
*pCtrl
)
269 pCtrl
->bs_access
= const_cast<Hook
*>(hAccess
.GetHook());
270 pCtrl
->check_mpeg
= false;
271 pCtrl
->stream_buffer_size
= 0;
272 pCtrl
->layer_3
.force_mono
= false;
273 pCtrl
->layer_3
.stereo
.freq_div
= 1;
274 pCtrl
->layer_3
.stereo
.freq_max
= 44100;
275 pCtrl
->layer_3
.stereo
.quality
= 2;
276 pCtrl
->layer_3
.mono
.freq_div
= 1;
277 pCtrl
->layer_3
.mono
.freq_max
= 44100;
278 pCtrl
->layer_3
.mono
.quality
= 2;
279 pCtrl
->layer_1_2
.force_mono
= false;
280 pCtrl
->layer_1_2
.stereo
.freq_div
= 1;
281 pCtrl
->layer_1_2
.stereo
.freq_max
= 44100;
282 pCtrl
->layer_1_2
.stereo
.quality
= 2;
283 pCtrl
->layer_1_2
.mono
.freq_div
= 1;
284 pCtrl
->layer_1_2
.mono
.freq_max
= 44100;
285 pCtrl
->layer_1_2
.mono
.quality
= 2;
288 bool rMP3Audio::checkMP3(BPTR file
, EDtError
&rc
)
290 unsigned char *buf
= 0;
298 buf
= new unsigned char[131072];
302 len
= DOS
->Read(file
, buf
, 131072);
306 rc
= DT_FileMalformed
;
310 // if we havent found anything within first 8k, there is no valid stream.
311 // we search only first 8kB for starting frame, but
312 // then we look up the whole 128kB for frame headers.
316 while (offset
< 8192)
318 framelen
= verifyFrame(&buf
[offset
], rc
);
324 while (sync
< 131000) // do not increase!
326 framelen
= verifyFrame(&buf
[sync
], rc
);
354 DOS
->Seek(file
, 0, OFFSET_BEGINNING
);
358 long rMP3Audio::verifyFrame(unsigned char* frame
, EDtError
&rc
)
363 if (frame
[0] != 0xff) // check sync
368 if ((frame
[1] & 0xfa) != 0xfa) // check valid type (mpeg 1 layer 3)
370 rc
= DT_FileFormatNotSupported
;
373 if ((frame
[2] & 0xf0) == 0xf0) // check valid bit rate
377 if ((frame
[2] & 0xc) != 0x0) // check valid freq
379 rc
= DT_WrongFrequency
;
382 if ((frame
[3] & 0xc0) == 0xc0) // check valid mode (stereo)
384 rc
= DT_WrongChannelCount
;
388 switch ((frame
[2]&0xf0) >> 4)
390 case 1: bitrate
= 32; break;
391 case 2: bitrate
= 40; break;
392 case 3: bitrate
= 48; break;
393 case 4: bitrate
= 56; break;
394 case 5: bitrate
= 64; break;
395 case 6: bitrate
= 80; break;
396 case 7: bitrate
= 96; break;
397 case 8: bitrate
= 112; break;
398 case 9: bitrate
= 128; break;
399 case 10: bitrate
= 160; break;
400 case 11: bitrate
= 192; break;
401 case 12: bitrate
= 224; break;
402 case 13: bitrate
= 256; break;
403 case 14: bitrate
= 320; break;
405 pad
= (frame
[2]&02) ? 1 : 0;
407 //DPrintfA("I think I found sync. Frame len: %ld bytes\n", ((1440*bitrate)/441)+pad);
409 return ((1440*bitrate
)/441)+pad
;
412 bool rMP3Audio::setUp()
416 pStream
= pMPEGA
->MPEGA_open(getFileName(), &hControl
);
418 ASSERT(NULL
!= pStream
);
426 err
= pMPEGA
->MPEGA_seek(pStream
, 1);
427 (void)err
; // Unused if not debugging
428 _D(Lvl_Info
, "%s: Initialized MPEGA stream (%08lx): error code %ld",
429 (int)__PRETTY_FUNCTION__
,
435 void rMP3Audio::cleanUp()
438 pMPEGA
->MPEGA_close(pStream
);
440 _D(Lvl_Info
, "%s: MPEGA stream closed.",
441 (int)__PRETTY_FUNCTION__
);
444 bool rMP3Audio::readData(const IOptItem
* item
, void* buff
, int lBlocks
)
446 uint16
*pBuff
= (uint16
*)buff
;
447 uint32 lLen
= lBlocks
* getBlockSize();
449 ASSERT(NULL
!= pStream
);
455 if (lPCMOffset
>= lSamples
)
459 // if anything fails, do not return garbage, ok?
460 lSamples
= pMPEGA
->MPEGA_decode_frame(pStream
, pPCM
);
461 _D(Lvl_Info
, "%s: Decoding MPEGA frames (stream %lx, buffer %lx): %ld",
462 (IPTR
)__PRETTY_FUNCTION__
,
468 if (lSamples
!= MPEGA_ERR_EOF
)
473 request("Info", "Its been 64 'faulty' frames now and most likely it is going to increase\nI doubt this is the problem with MP3 file (but who knows?)\nIf problem persists, mail the brilliant author of your mpega.library\nI keep getting %ld.", "Proceed", ARRAY((IPTR
)lSamples
));
476 lSamples
= MPEGA_PCM_SIZE
;
477 memset(pPCM
[0], 0, MPEGA_PCM_SIZE
);
478 memset(pPCM
[1], 0, MPEGA_PCM_SIZE
);
480 // do nothing. things will not get copied anyways
483 // clone data to target buffer. remember to reverse byte order.
484 while (lPCMOffset
<lSamples
)
486 // clone reversed samples.
487 *pBuff
= FLIP16(pPCM
[0][lPCMOffset
]);
489 *pBuff
= FLIP16(pPCM
[1][lPCMOffset
]);
493 // this loop will never begin with lLen = 0. see outer while loop.
502 uint
rMP3Audio::access(void* , MPEGA_ACCESS
*acc
)
508 case MPEGA_BSFUNC_OPEN
:
510 * since our stream is already open, we won't re-open it again.
512 acc
->data
.open
.stream_size
= stream_size
;
513 DOS
->Seek(handle
, stream_start
, OFFSET_BEGINNING
);
514 current_location
= 0;
515 _D(Lvl_Info
, "%s: MPEGA_OPEN - Opening virtual stream. Current location: %ld", (uint
)__PRETTY_FUNCTION__
, current_location
);
518 case MPEGA_BSFUNC_CLOSE
:
520 * do nothing. we close everything on dispose()
522 _D(Lvl_Info
, "%s: MPEGA_CLOSE - Disposing virtual stream.", (uint
)__PRETTY_FUNCTION__
);
525 case MPEGA_BSFUNC_READ
:
527 * that is fairly easy. just read()
529 if (current_location
>= stream_size
)
531 _D(Lvl_Info
, "%s: MPEGA_READ - no more data", (uint
)__PRETTY_FUNCTION__
);
535 d
= DOS
->Read(handle
, acc
->data
.read
.buffer
,
536 (unsigned)acc
->data
.read
.num_bytes
< (stream_size
- current_location
) ? (unsigned)acc
->data
.read
.num_bytes
: (stream_size
- current_location
));
537 current_location
+= d
;
538 _D(Lvl_Info
, "%s: MPEGA_READ - Read %ld bytes. Advancing position to %ld.", (uint
)__PRETTY_FUNCTION__
, d
, current_location
);
541 case MPEGA_BSFUNC_SEEK
:
542 current_location
= ((unsigned)acc
->data
.seek
.abs_byte_seek_pos
< stream_size
? (unsigned)acc
->data
.seek
.abs_byte_seek_pos
: stream_size
);
543 _D(Lvl_Info
, "%s: MPEGA_SEEK - Seeking to relative location %ld.", (uint
)__PRETTY_FUNCTION__
, current_location
);
544 DOS
->Seek(handle
, current_location
+ stream_start
, OFFSET_BEGINNING
);
548 _D(Lvl_Warning
, "%s: MPEGA_xxx - Your MPEGA library just asked me to do something I never heard of (%ld)", (uint
)__PRETTY_FUNCTION__
, acc
->func
);
549 ASSERTS(false, "Your mpega.library has just asked me\nto do something funny.");