📄 riptracks.c
字号:
/*
* riptracks.c - Copyright (C) 1999,2000 Jay A. Key
*
* Contains the RipTrack() function. The rip dialog proc, plus the
* read and encode threads are all contained in this file. The read
* thread is also responsible for jitter correction. A good example of
* how to rip a track from a CD via AKRip32.dll
*
**********************************************************************
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <windows.h>
#include <stdio.h>
#include <time.h>
#include <malloc.h>
#include "akrip/akrip32.h"
#include "resources.h"
#include "globals.h"
#include "riptracks.h"
#include "trackwnd.h"
#include "bladedll.h"
#include "gauge.h"
#include "id3.h"
#define _USE_LAME 0
DWORD RipThread( LPENCODETHREAD pet );
DWORD EncodeThread( LPENCODETHREAD pet );
LPTRACKBUF newTrackBuf( DWORD numFrames );
void RipTrack( HWND hDlg, int idx, LPADDTRACK lpAddTrack );
int writeAndFlush( FILE *fp, BYTE *buf, int len );
void writeWavHeader( FILE *fp, DWORD len );
BOOL loadBladeEnc( void );
void setRipTrackStatus( LPENCODETHREAD e );
BOOL LoadEncoderFunctions( ENCODER nEncoder );
void UpdateTime (long numWritten);
BOOL isOldLameEnc( void );
static ENCODETHREAD e;
static ADDTRACK rtAddTrack;
//static BOOL bSecond = FALSE;
/*
* 1. initializes the common data structure
* 2. starts the threads
*
*
*/
void RipTrack( HWND hDlg, int idx, LPADDTRACK lpAddTrack )
{
DWORD dwRipThreadID, dwEncThreadID;
static DWORD numBufFrames = 8;
static char outputName[MAX_PATH+1];
int iDirLen; // iNameLen, iTotal;
char *p;
ZeroMemory( &e, sizeof(e) );
e.idx = idx;
time( &e.tstart );
e.hDlg = hDlg;
wsprintf( outputName, "%s.%s", lpAddTrack->name, bMP3?"mp3":"wav" );
SetDlgItemText( hDlg, IDT_TRACKNAME, outputName );
// we really need to check filename lengths here, but I'm just too damn
// tired right now
iDirLen = lstrlen( szMP3OutputDir );
//iNameLen = lstrlen( lpAddTrack->name );
//iTotal = iDirLen + iNameLen + 5;
if ( bMP3 )
lstrcpy( outputName, szMP3OutputDir );
else
lstrcpy( outputName, szWavOutputDir );
iDirLen = lstrlen( outputName );
if ( !iDirLen )
lstrcpy( outputName, ".\\" );
if ( iDirLen && outputName[iDirLen-1] != '\\' )
lstrcat( outputName, "\\" );
// add the track name, and then replace illegal characters with a '_'
p = outputName + lstrlen( outputName );
lstrcat( outputName, lpAddTrack->name );
while( *p )
{
if ( !validFnameChar[(unsigned char)*p] )
*p = '_';
p++;
}
// add the proper extension
p = outputName + lstrlen( outputName );
lstrcat( outputName, bMP3?".mp3":".wav" );
e.fpOut = fopen( outputName, "w+b" );
if ( !bMP3 )
writeWavHeader( e.fpOut, 0 );
if ( bMP3 && bWavMirror )
{
// change the extension to WAV (p was set to the terminating NULL)
lstrcpy( p, ".wav" );
e.fpWavMirror = fopen( outputName, "wb" );
writeWavHeader( e.fpWavMirror, 0 );
}
else
e.fpWavMirror = NULL;
if ( bMP3 )
{
switch( iEncoder )
{
case BLADE_ENC_DLL:
wsprintf( outputName, "%d kbps MP3 via BladeEnc DLL", wBitrate );
break;
case LAME_ENC_DLL:
if ( isOldLameEnc() )
{
e.bOldLame = TRUE;
}
wsprintf( outputName, "%d kbps MP3 via Lame_Enc DLL", wBitrate );
if ( bVBR && !e.bOldLame )
wsprintf( outputName+lstrlen(outputName),
", VBR max bitrate %d, VBR quality %d", wMaxBitrate,
nVBRQuality );
break;
}
}
else
wsprintf( outputName, "WAV file" );
SetDlgItemText( hDlg, IDT_OUTPUTOPTTEXT, outputName );
ModifyCDParms( hCD, CDP_OVERLAP, numOverlap );
ModifyCDParms( hCD, CDP_JITTER, jitterCheck );
ModifyCDParms( hCD, CDP_READMODE, readMode );
e.startFrame = lpAddTrack->start;
e.trackLen = lpAddTrack->len;
e.endFrame = e.startFrame + e.trackLen;
InitializeCriticalSection( &e.cs );
e.hRipCancel = CreateEvent( NULL, FALSE, FALSE, NULL );
SendDlgItemMessage( hDlg, IDG_RIPPROG, GM_SETRANGE,
(WPARAM)((UINT)e.trackLen * 2352),
0L );
SendDlgItemMessage( hDlg, IDG_RIPPROG, GM_SETPOS, 0, 0L );
wrqInitQueue( &e.q, maxRip * 2352 * numBufFrames );
SendDlgItemMessage( hDlg, IDG_READBUF, GM_DISPPCT, (WPARAM)FALSE, 0L );
SendDlgItemMessage( hDlg, IDG_READBUF, GM_SETRANGE,
(WPARAM)wrqFreeSpace( &e.q ), 0L );
// Set ID3 info for track. Album title and artist are set elsewhere
asSetID3Info( ID3_TITLE, lpAddTrack->name, 0 );
asSetID3Info( ID3_LEVEL, NULL, 1 );
#if 0
asSetID3Info( ID3_YEAR, "1997", 0 );
asSetID3Info( ID3_GENRE, NULL, 17 );
#endif
e.aHandles[0] = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)RipThread,
(LPVOID)&e, 0, &dwRipThreadID );
e.aHandles[1] = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)EncodeThread,
(LPVOID)&e, 0, &dwEncThreadID );
}
BOOL RipTrackDlgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
WORD wID;
//static BOOL bDone = FALSE;
static HWND hTrckWnd = NULL;
int idx = -1;
switch( uMsg )
{
case WM_DESTROY:
bRippingTracks = FALSE;
EndDialog( hWnd, 0 );
break;
case WM_TRACKDONE:
//bSecond = TRUE;
DeleteCriticalSection( &e.cs );
//CloseHandle( &e.hRipCancel );
wrqDeinitQueue( &e.q );
// set the status text for the last track ripped
setRipTrackStatus( &e );
// if more tracks to rip and the last one finished successfully,
// call RipTrack again
idx = SendMessage( hTrckWnd,WM_FINDNEXTTRACK,0,(LPARAM)&rtAddTrack);
if ( (idx != -1) && (e.status == EST_SUCCESS) )
RipTrack( hWnd, idx, &rtAddTrack );
else
{
bRippingTracks = FALSE;
EndDialog( hWnd, 0 );
}
break;
case WM_INITDIALOG:
//bDone = FALSE;
//bSecond = FALSE;
hTrckWnd = (HWND)lParam;
bRippingTracks = TRUE;
idx = SendMessage(hTrckWnd,WM_FINDFIRSTTRACK,0,
(LPARAM)&rtAddTrack);
if ( idx != -1 )
{
RipTrack( hWnd, idx, &rtAddTrack );
}
else
{
MessageBox( GetParent(hTrckWnd), "No tracks selected", "Warning!",
MB_ICONEXCLAMATION | MB_OK );
bRippingTracks = FALSE;
EndDialog( hWnd, 0 );
}
break;
case WM_COMMAND:
wID = LOWORD( wParam );
if ( wID == IDBN_CANCELRIP || wID == 2 )
{
if ( /* bDone || */ (MessageBox( hWnd, "Really stop?", "Abort?", MB_APPLMODAL | MB_YESNO | MB_ICONEXCLAMATION ) == IDYES) )
{
e.bForceRipExit = TRUE;
e.status = EST_ABORTED;
SetEvent( e.hRipCancel );
EnableWindow( GetDlgItem( hWnd, IDBN_CANCELRIP ),FALSE );
}
}
return TRUE;
}
return FALSE;
}
BOOL RipTrackSegmentDlgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
WORD wID;
switch( uMsg )
{
case WM_DESTROY:
bRippingTracks = FALSE;
EndDialog( hWnd, 0 );
break;
case WM_TRACKDONE:
DeleteCriticalSection( &e.cs );
wrqDeinitQueue( &e.q );
if ( e.status != EST_SUCCESS )
{
}
bRippingTracks = FALSE;
EndDialog( hWnd, 0 );
break;
case WM_INITDIALOG:
bRippingTracks = TRUE;
RipTrack( hWnd, -1, (LPADDTRACK)lParam );
break;
case WM_COMMAND:
wID = LOWORD( wParam );
if ( wID == IDBN_CANCELRIP || wID == 2 )
{
if ( MessageBox( hWnd, "Really stop?", "Abort?", MB_APPLMODAL | MB_YESNO | MB_ICONEXCLAMATION ) == IDYES )
{
e.bForceRipExit = TRUE;
e.status = EST_ABORTED;
SetEvent( e.hRipCancel );
EnableWindow( GetDlgItem( hWnd, IDBN_CANCELRIP ),FALSE );
}
}
return TRUE;
}
return FALSE;
}
LPTRACKBUF newTrackBuf( DWORD numFrames )
{
LPTRACKBUF t;
int numAlloc;
numAlloc = (((int)numFrames)*2352) + TRACKBUFEXTRA;
t = (LPTRACKBUF)malloc( numAlloc );
if ( !t )
return NULL;
t->startFrame = 0;
t->numFrames = 0;
t->maxLen = numFrames * 2352;
t->len = 0;
t->status = 0;
t->startOffset = 0;
return t;
}
//#define _ALWAYS_READ_MAX
/*
* Thread responsible for reading the data from the CD and placing it in
* a queue for the encode thread.
*
* trying a new strategy -- always read the full maxRip frames, and truncate
* if it's too many, but after the read. Some of the SCSI read modes were
* crapping out on the last read when I suddenly requested a different number
* of frames. To switch back to the old method, undefine _ALWAYS_READ_MAX
*
* CDRM_JITTERONERR uses two full-size TRACKBUFs on a rotating basis. On the
* read it uses t1, and then on the next read switches to t2. If an error
* is returned from the read function, then the buffer from the previous read
* is used to jitter correct the current one.
*/
DWORD RipThread( LPENCODETHREAD pet )
{
LPTRACKBUF tbuf, t1, t2, tover, tTmp;
DWORD num2rip, dwStatus;
HANDLE hWait[2];
int retries;
BOOL useT1 = FALSE;
HWND hReadGauge;
BOOL bWaited;
//char buf[81];
hWait[0] = CreateEvent( NULL, FALSE, FALSE, NULL );
hWait[1] = pet->hRipCancel;
// initialize all buffers
tbuf = t1 = t2 = tover = tTmp = NULL;
tbuf = t1 = newTrackBuf( maxRip );
switch ( readMode )
{
case CDRM_JITTER:
tover = newTrackBuf( numOverlap );
break;
case CDRM_JITTERONERR:
t2 = newTrackBuf( maxRip );
break;
default:
case CDRM_NOJITTER:
readMode = CDRM_NOJITTER;
break;
}
num2rip = maxRip;
hReadGauge = GetDlgItem( e.hDlg, IDG_READBUF );
while( TRUE )
{
bWaited = FALSE;
if ( e.startFrame >= e.endFrame )
break;
if ( e.bForceRipExit )
{
e.bForceEncExit = TRUE;
goto asRipExit;
}
EnterCriticalSection( &e.cs );
if ( e.endFrame - e.startFrame < maxRip )
num2rip = e.endFrame - e.startFrame;
LeaveCriticalSection( &e.cs );
while( wrqFreeSpace( &e.q ) < 2352 * num2rip )
{
bWaited = TRUE;
if ( e.bForceRipExit )
{
e.bForceEncExit = TRUE;
goto asRipExit;
}
ResetEvent( hWait[0] );
wrqSetWait( &e.q, hWait[0], 2352 * maxRip * 6 );
//WaitForSingleObject( hWait, 20000 );
WaitForMultipleObjects( 2, hWait, FALSE, 60000 );
if ( e.bForceRipExit )
{
e.bForceEncExit = TRUE;
goto asRipExit;
}
}
switch( readMode )
{
case CDRM_JITTER:
EnterCriticalSection( &e.cs );
tbuf->startFrame = e.startFrame;
LeaveCriticalSection( &e.cs );
#ifndef _ALWAYS_READ_MAX
tbuf->numFrames = num2rip;
#else
tbuf->numFrames = maxRip;
#endif
tbuf->startOffset = 0;
tbuf->len = 0;
//retries = 3;
//dwStatus = SS_ERR;
for( dwStatus = SS_ERR, retries = 3; dwStatus != SS_COMP && retries; retries-- )
{
dwStatus = ReadCDAudioLBAEx( hCD, tbuf, tover );
}
break;
case CDRM_JITTERONERR:
if ( useT1 )
{
tbuf = t1;
tTmp = t2;
}
else
{
tbuf = t2;
tTmp = t1;
}
EnterCriticalSection( &e.cs );
tbuf->startFrame = e.startFrame;
LeaveCriticalSection( &e.cs );
#ifndef _ALWAYS_READ_MAX
tbuf->numFrames = num2rip;
#else
tbuf->numFrames = maxRip;
#endif
tbuf->startOffset = 0;
tbuf->len = 0;
// try to force jitter correction after a wait
if ( !bWaited )
dwStatus = ReadCDAudioLBA( hCD, tbuf );
else
dwStatus = SS_ERR;
// after an error or a wait, dwStatus will be SS_ERR
if ( dwStatus == SS_ERR )
{
if ( tTmp->len )
{
tTmp->startOffset += ((tTmp->numFrames - jitterCheck)*2352);
tTmp->startFrame += ( tTmp->numFrames - jitterCheck );
tTmp->numFrames = jitterCheck;
tTmp->len = jitterCheck * 2352;
}
else
tTmp->len = tTmp->startOffset = tTmp->numFrames;
for( retries = 3; (dwStatus != SS_COMP) && retries; retries-- )
{
dwStatus = ReadCDAudioLBAEx( hCD, tbuf, tTmp );
}
}
useT1 = !useT1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -