월천 박영목입니다... ㅋㅋㅋ 행복하시지요... 저도 행복합니다.. 감사합니다.
(소스, dll, 실행파일 올렸습니다... 수정했음..)
지금... 매장음악방송 쪽에서 일합니다. 면접 볼 때 프로그램 조금만 하고 관리만 한번씩 하면 된다..
그래서 월급도 많이 깍아주고.. 이제 노년을 좀 편히 보내자 하고 그냥 들어왔는데.. 이거 뭐...
제가 보니 할 것이 엉청나게 많군요... ㅋㅋㅋ 플레이어도 다 다시 만들어야 할 판...... ㅋㅋㅋ
매장들이 인터넷 환경이 않좋아 실시간 플레이가 힘던 곳이 제법 있고.. 그런데 core쪽 소스는
dll로 다 감추어져 있고(외주, Win32, 다음에도 VC로 해야함) 수정하려고 해도... 인터페이스 약간...
모니터링 쪽은 실시간도 힘들고..
다른 일들이 바빠서... 그런데 음원 파일을 변환하는 것도 장난이 아니더군요... 몇 만곡씩 되니..
MP3도 Bitrate별로 있어야 하고... 이것도 일반적인 변환프로그램으로 돌리는 데... 신경이 너무 쓰인
다고.. ㅋㅋㅋ 그래서 그런 것 만들면 되는 데.. 했더니 만들어라고... ㅋㅋㅋ 괘히 말했나...
이것 만들어 주니... WMA도 .. 쩝 그래서.. WMA도 ffmpeg로 간단히 만들어 주었더니... 나는 하나만
돌리는 줄 알았는데.. 이것을 8개를 돌리고 있더군요.. 서버가 죽어요...ㅋㅋㅋ. 기가 막힘... 그래서..
좀 더 효율 좋게... 그러면서 이쪽으로 공부 했습니다. 음악도 들어가며 음질 TEST도 하고...
소스에 적혀 있으니 참고 하시기 바랍니다.... 아참 그리고 가변 비트레이드 VBR 사용하지 마세요..
64K 에서는 쇠소리가 더 납니다. CBR로 하세요.. 용량도 그렇게 차이가 안납니다...
VBR은 가변이라 파일 용량적인 효과는 있겠지만... CPU가 힘들겠지요...
여러가지 호환적인 문제도 있고... 저는 사용하지 않았습니다.
그리고 64kbps 에서 모노나 스트레오.. 용량 차이가 거의 없습니다. 제가 모노로 하면 용량이 더 줄
것인데.. 하니.. 팀장이 그러더라구요.. 하실 수 있으면 해보시라고... 그래서 용량을 더 줄려 보려고
변환 프로그램으로 모노로 해도 거의 차이가 없었습니다. 왜 그렇지... 팀장도 이유는 모른다고 나도
생각 안남... 그래서 1시간 후에 화장실에 들어가는 순간 그 냄새가... 그 비밀을 알려 주었습니다.
ㅋㅋㅋ 화장실 자주 가세요,,, 안되면 ㅋㅋㅋ 분명한 효과가 있습니다. ㅋㅋㅋ 간단한 것인데...
MP3은 손실 압축이지요... 그런데 64K까지 내리면서 톡톡 튀는 놈들은 둘글게 깎여서... 2개의 파형
값이 거의 동일하게 바뀌게 되지요... 그리고 여기에 압축을 해버리니... 1+1 = 거의 1 이 되지요...
압축이니.. 같은 것은 1개로 만들어 버리니.. 간단한 것인데... ㅋㅋㅋ....
그런데 음질은 모노가 더 좋았습니다. 스테레오보다.,.. 그런데 JSTEREO로 하니 동일한 것으로
들렸음.... ㅋㅋㅋ ... 에고 부산역 분수 프로그램은 언제 만드나,.,, 날짜는 다가 오고... ㅋㅋㅋ
물러갑니다... 부산에서.... 월천 박영목 올렸습니다. 다들 행복하세요.. ^^
//---------------------------------------------------------------------------
#include <vcl.h>
#include <process.h>
#include <stdio.h>
#pragma hdrstop
#include "basswma.h"
#include "lame.h"
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "bass.lib"
#pragma link "basswma.lib"
#pragma link "lame_enc.lib"
#pragma link "CGAUGES"
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
bool __fastcall DeleteFilePerfect( AnsiString asDes )
{
FileSetReadOnly( asDes, false ); //파일속성을 Normal로 바꾸고
bool bRet = DeleteFile( asDes );
return bRet;
}
void __fastcall TForm1::ShowStatus( AnsiString Msg )
{
if( MemoStatus->Lines->Count>50 )
MemoStatus->Lines->Delete( 0 );
MemoStatus->Lines->Add( Msg );
}
void __fastcall TForm1::WMStartConv( TMessage msg )
{
switch( msg.WParam )
{
/*
case 192 : //이렇게 변환할 분들은 하시고..
CGauge192->MaxValue = 100;
CGauge192->MinValue = 0;
CGauge192->Progress = 0;
break;
case 128 :
CGauge128->MaxValue = 100;
CGauge128->MinValue = 0;
CGauge128->Progress = 0;
break;
case 64 :
CGauge64->MaxValue = 100;
CGauge64->MinValue = 0;
CGauge64->Progress = 0;
break;
*/
CGauge1->MinValue = 0;
CGauge1->Progress = 0;
}
}
void __fastcall TForm1::WMCurConv( TMessage msg )
{
switch( msg.WParam )
{
CGauge1->Progress = msg.LParam;
}
}
void __fastcall TForm1::WMEndConv( TMessage msg )
{
//TListItems *ltItems;
AnsiString asMsg;
//switch( msg.WParam )
//{
if( msg.LParam==1 )
ShowStatus( "INF: OK" );
else
ShowStatus( "ERR: Failure" );
// break;
//}
}
void __fastcall SendPostMsg( HWND hMain, DWORD dwMsg, DWORD dwBitrate, DWORD dwStatus )
{
int i=15;
while( i-- )
{
if( ::PostMessage( hMain, dwMsg, (DWORD)dwBitrate, dwStatus )==true )
break;
Sleep( 100 );
}
}
typedef struct ST_CONV
{
HWND hMain;
AnsiString asInFile;
AnsiString asOutFile;
DWORD dwBitrate;
DWORD dwSampleRate;
} stConv;
static unsigned __stdcall FuncThreadConv( void* lParam )
{
stConv *stData = (stConv*)lParam;
HWND hMain = stData->hMain;
AnsiString asInFullFileName = stData->asInFile;
AnsiString asOutFullFileName = stData->asOutFile;
DWORD dwWantBitrate = stData->dwBitrate;
DWORD dwSampleRate = stData->dwSampleRate;
delete stData;
//----------------------------------------------------------------------------
int iSleep = 4;
if( dwWantBitrate==64 ) iSleep = 2; //이것은 쓰레드로 돌릴 떄 64rk 더 오래 걸려서... 좀 빠르게 한 것입니다. 빼셔도 됨... 회사에 맞게 맞추다 보니 들어갔네요...
DWORD dwStatus = 0;
//BE_VERSION Version = { 0, };
BE_CONFIG beConfig = { 0, };
DWORD dwSamples = 0;
DWORD dwMP3Buffer = 0;
BE_ERR err = 0;
FILE* pFileOut = NULL;
HBE_STREAM hbeStream = 0;
PBYTE pMP3Buffer = NULL;
PSHORT pWAVBuffer = NULL;
DWORD chan;
//----------------------------------------------------------------------------
float time;
int iPlayTime=0;
chan = BASS_WMA_StreamCreateFile( false, asInFullFileName.c_str(),0,0,BASS_STREAM_DECODE );
if( chan==0 )
{
SendPostMsg( hMain, WM_USER_END_PROCESS, dwWantBitrate, dwStatus );
_endthreadex( GetCurrentThreadId() );
return -11; //입력 MP3 열기 실패
}
time = BASS_ChannelBytes2Seconds( chan, BASS_ChannelGetLength( chan, BASS_POS_BYTE ) ); // playback duration
iPlayTime = (time*1000) + 0.5; //MP3 연주전체시간
if( iPlayTime==0 )
{
if( chan )
BASS_StreamFree( chan );
SendPostMsg( hMain, WM_USER_END_PROCESS, dwWantBitrate, dwStatus );
_endthreadex( GetCurrentThreadId() );
return -11; //입력 MP3 열기 실패
}
//---------------------------------------------------------------------------
BASS_INFO bass_info;
BASS_CHANNELINFO bass_channel_info;
bool bRet = BASS_GetInfo( &bass_info );
DWORD len = BASS_StreamGetFilePosition( chan, BASS_FILEPOS_END ); //file length
DWORD BitrateOfMP3 = (DWORD)(len/(125*time)+0.5); //bitrate (Kbps) 125가 뭔지 모르겠음... 아는 사람 연락바람... ㅋㅋㅋ
//---------------------------------------------------------------------------
//아래까지 내려오면 만약에 iBitRate의 값에 근접한다면 그냥 파일을 복사한다.
//근접한다는 범위를 어떻게 결정할 것인가?
//비트레이트가 낮은 것은 아래 것은
//파일의 비트레이트를 가지고 온 것이 192짜리이다.
//그러면 192는 그냥 복사를 한다. 128, 64는 변환을 한다.
//128이다 192, 128 그냥 복사, 64변환....
//64보다 더 낮은 것은 그냥 다 복사를 한다.
//16 이상 차이가 나야 한다.
//int iCab = BitrateOfMP3 - dwWantBitrate; //현재 파일의 Bitrate - 바꾸기를 원하는 Bitrate
//if( iCab<0 ) iCab *= -1;
//if( (iCab<=16) || (dwWantBitrate>BitrateOfMP3) ) //bitrate의 차가 16보다 작거나 변환하고자 하는 bitrate가 더 클 경우 그냥 복사한다.
//{
// if( chan )
// BASS_StreamFree( chan );
// bool bSuccess=false;
//DeleteFilePerfect( asOutFullFileName );
// bSuccess = CopyFileTo( asInFullFileName, asOutFullFileName ); //bSuccess 믿을 수 없음...
// if( GetLargeFileSize( asInFullFileName ) == GetLargeFileSize( asOutFullFileName ) )
// dwStatus = 1;
// SendPostMsg( hMain, WM_USER_END_PROCESS, dwWantBitrate, dwStatus );
// _endthreadex( GetCurrentThreadId() );
// return 1; //성공: 파일 복사 or Thread 시작
//}
//else
{
bRet = BASS_ChannelGetInfo( chan, &bass_channel_info );
//---------------------------------------------------------------------------
// use the LAME config structure
beConfig.dwConfig = BE_CONFIG_LAME;
// this are the default settings for testcase.wav
beConfig.format.LHV1.dwStructVersion = 1;
beConfig.format.LHV1.dwStructSize = sizeof(beConfig);
beConfig.format.LHV1.dwSampleRate = bass_channel_info.freq; //현재 원본 파일의 SampleRate이다. 여기에 dwSampleRate 이 값을 넣을 경우 나도 넣었다. ㅋㅋㅋ 바꿀 샘플레이트가 동일하면 아무 이상 없지만 원본이 클 경우 연주 시간이 늘어나게 된다. 물론 음도 모두 늘어나게 된다.
if( dwWantBitrate<=64 ) beConfig.format.LHV1.dwReSampleRate = 0;
else beConfig.format.LHV1.dwReSampleRate = dwSampleRate; //64이하 일 경우는 '0'으로 주어 Encoder가 알아서 하게 두면 음질이 더 좋습니다. 그렇지 않고 강제로 조정할 경우 음질이 약간 쇠소리를가 흐르게 된다. 물론 자세히 들어야 하지만 그러나 느낌이 싱글럽다는 느낌을 느낄 수 있다.
if( bass_channel_info.chans==1 )
beConfig.format.LHV1.nMode = BE_MP3_MODE_MONO;
else
beConfig.format.LHV1.nMode = BE_MP3_MODE_JSTEREO;
beConfig.format.LHV1.dwBitrate = dwWantBitrate;
beConfig.format.LHV1.nPreset = LQP_NOPRESET;
beConfig.format.LHV1.nQuality = LQP_VERYHIGH_QUALITY;
beConfig.format.LHV1.dwMpegVersion = MPEG1; // MPEG VERSION (I or II)
beConfig.format.LHV1.dwPsyModel = 0; // USE DEFAULT PSYCHOACOUSTIC MODEL
beConfig.format.LHV1.dwEmphasis = 0; // NO EMPHASIS TURNED ON
beConfig.format.LHV1.bOriginal = FALSE; //TRUE; // SET ORIGINAL FLAG
beConfig.format.LHV1.bWriteVBRHeader = FALSE; //TRUE; // Write INFO tag
beConfig.format.LHV1.dwMaxBitrate = dwWantBitrate; //128; //320; // MAXIMUM BIT RATE
beConfig.format.LHV1.bCRC = FALSE; //TRUE; // INSERT CRC
beConfig.format.LHV1.bCopyright = FALSE; //TRUE; // SET COPYRIGHT FLAG
beConfig.format.LHV1.bPrivate = FALSE; //TRUE; // SET PRIVATE FLAG
beConfig.format.LHV1.bWriteVBRHeader = FALSE; //TRUE; // YES, WRITE THE XING VBR HEADER
beConfig.format.LHV1.bEnableVBR = FALSE; //TRUE; // USE VBR
beConfig.format.LHV1.nVBRQuality = 0; //5; // SET VBR QUALITY
beConfig.format.LHV1.bNoRes = FALSE; //TRUE; // No Bit resorvoir
// Init the MP3 Stream
err = beInitStream( &beConfig, &dwSamples, &dwMP3Buffer, &hbeStream );
// Check result
if( err != BE_ERR_SUCCESSFUL )
{
SendPostMsg( hMain, WM_USER_END_PROCESS, dwWantBitrate, dwStatus );
_endthreadex( GetCurrentThreadId() );
return -21; //Failure... MP3 출력 beInitStream
}
//--------------------------------------------------------------------------
// Allocate MP3 buffer
pMP3Buffer = new BYTE[dwMP3Buffer+10];
//------------------------------------
// Allocate WAV buffer
pWAVBuffer = new SHORT[dwSamples+1];
// Check if Buffer are allocated properly
if( !pMP3Buffer || !pWAVBuffer ) //메모리 확보를 못했으면.... 빠져나감..
{
if( pMP3Buffer ) delete []pMP3Buffer;
if( pWAVBuffer ) delete []pWAVBuffer;
if( chan )
BASS_StreamFree( chan );
if( hbeStream )
beCloseStream( hbeStream );
SendPostMsg( hMain, WM_USER_END_PROCESS, dwWantBitrate, dwStatus );
_endthreadex( GetCurrentThreadId() );
return 0; //일반적인 실패
}
//---------------------------------------------------------------------------
pFileOut= fopen( asOutFullFileName.c_str() ,"wb+");
// Check file open result
if( pFileOut==NULL )
{
if( pMP3Buffer ) delete []pMP3Buffer;
if( pWAVBuffer ) delete []pWAVBuffer;
if( chan )
BASS_StreamFree( chan );
if( hbeStream )
beCloseStream( hbeStream );
SendPostMsg( hMain, WM_USER_END_PROCESS, dwWantBitrate, dwStatus );
_endthreadex( GetCurrentThreadId() );
return -1; //Failure Open OutFile
}
}
//----------------------------------------------------------------------------
//메인에게 변환을 시작한다고 알린다. 그리고 시작한다.
SendPostMsg( hMain, WM_USER_START_PROCESS, (DWORD)dwWantBitrate, dwStatus );
bool bStart = true; //쓰레드 공유
//----------------------------------------------------------------------------
DWORD nr, dwWrite;
DWORD inc=0;
QWORD c, pos;
QWORD qwTotLength = BASS_ChannelGetLength(chan, BASS_POS_BYTE);
int iProcess;
float dwDDD = 0;
int iStore=0;
int i=1;
while( 1 ) //BASS_ChannelIsActive( chan ) ) //BASS_ChannelGetData( chan, pWAVBuffer, dwSamples * 2 )>0 ) //SizeOf(SmallInt));
{
if( bStart==true )
{
if( BASS_ChannelIsActive( chan ) )
{
c = BASS_ChannelGetData( chan, pWAVBuffer, dwSamples * 2 ); //c = BASS_ChannelGetData( chan, pWAVBuffer, dwSamples * 2 ); pIMSIWAVBuffer
if( c==-1 )
{
bStart = false;
continue;
}
err = beEncodeChunk( hbeStream, dwSamples, pWAVBuffer, pMP3Buffer, &dwWrite ); // dwSamples
if( err==BE_ERR_SUCCESSFUL )
{
if( fwrite(pMP3Buffer,1,dwWrite,pFileOut) != dwWrite )
{
bStart = false;
continue;
}
else
{
dwDDD += c;
iProcess = (dwDDD/qwTotLength) * 100;
::PostMessage( hMain, WM_USER_CUR_PROCESS, (DWORD)dwWantBitrate, iProcess );
}
}
else
{
bStart = false;
continue;
}
::Sleep( iSleep );
}
else
{
err = beDeinitStream( hbeStream, pMP3Buffer, &dwWrite );
if( err!=BE_ERR_SUCCESSFUL )
{
bStart = false;
continue;
}
else
{
if( dwWrite )
{
if( fwrite( pMP3Buffer,1,dwWrite,pFileOut ) != dwWrite )
{
bStart = false;
continue;
}
}
}
fclose( pFileOut );
pFileOut = NULL;
::Sleep(1000); //이것은 파일을 닫는 부분을 윈도우에서 일을 잘 처리하라고 주는 것입니다. 한번씩 이런 것 주세요...
::Sleep(1000); //전전 회사에서 설치프로그램(NSIS)에 이런 것 넣지 않아 제가 들어간 후 3일만에 팀장 짤렸습니다. 분명이 파일을 지우고 떺어 쉬우게 해 두었는데.
//지우자 말자 바로 쓰니... 쓰지지 않지... 이것 때문에 짤렸으니... 자기도 어떤 때는 되고 어떤 때는 안된다고... 소스 좀 봐달라고 하면 봐주었을 것인데..
//마우스도 못만지게 하니... 그때 생각하면 마음이 아픔..... 지금 팀장은 논리적이고 사람이 좋음... ㅋㅋㅋ
//-----------------------------------------------------------------------
float fplaytime = 0;
int iTotTime = 0;
DWORD dwchan;
dwchan = BASS_StreamCreateFile( false, asOutFullFileName.c_str(),0,0,BASS_STREAM_DECODE );
if( dwchan==0 )
{
dwchan = BASS_MusicLoad( false, asOutFullFileName.c_str(), 0, 0, BASS_MUSIC_DECODE|BASS_MUSIC_RAMP|BASS_MUSIC_PRESCAN, 0 ); //BASS_MUSIC_RAMPS|BASS_MUSIC_POSRESET|BASS_MUSIC_PRESCAN
}
if( dwchan )
{
fplaytime = BASS_ChannelBytes2Seconds( dwchan, BASS_ChannelGetLength(dwchan, BASS_POS_BYTE) ); // playback duration
iTotTime = (fplaytime * 1000) + 0.5;
if( iTotTime>0 )
{
int iTimeCap = iPlayTime - iTotTime;
if( iTimeCap<0 ) iTimeCap *= -1;
if( iTimeCap<=2000 ) dwStatus=1;
}
BASS_StreamFree( dwchan );
}
bStart = false;
continue;
}
}
else //bStart
{
break;
}
}
//----------------------------------------------------------------------------
if( pMP3Buffer!=NULL )
delete []pMP3Buffer;
if( pWAVBuffer!=NULL )
delete []pWAVBuffer;
//----------------------------------------------------------------------------
if( chan )
{
BASS_StreamFree( chan );
chan = 0;
}
if( hbeStream )
{
beCloseStream( hbeStream );
hbeStream = 0;
}
if( pFileOut )
{
fclose( pFileOut );
pFileOut=NULL;
}
if( dwStatus==0 )
{
::Sleep( 1000 );
::Sleep( 1000 );
DeleteFilePerfect( asOutFullFileName );
}
SendPostMsg( hMain, WM_USER_END_PROCESS, dwWantBitrate, dwStatus );
_endthreadex( GetCurrentThreadId() );
return 0;
}
//FuncThreadConv 이 함수 보면 안에 같은 것들이 계속 나올 것입니다. 왜 이렇게 했는지. 묻지 마세요..
//빨리 만들어야 하는데... 반복되는 것 다 빼서 예쁘게 함수를 따로 만드니... 계속 Error가 발생,...
//그래서 겁이 나서 다 풀어 헤쳐 놓았음.... 1개는 잘 돌아는데 쓰레드 돌리면.. 이게 죽어... lame_enc 때문인 것 같은데...
//그래서 Error 안 나는 까지.. 다 넣어 버렸음.... ㅋㅋㅋㅋ 이유는 담에 알아봐요지요... 중첩인지.. 뭔지...
//지금은 바빠서.... 참 그리고 쓰레드 _beginthreadex으로 하세요... TThread로 하지 마시고 이것 때문에... 좀 힘들었음.. ㅋㅋㅋ
//이것도 lame_enc 때문인 것 같은데... ㅋㅋㅋㅋ 그레도 쓰레드 돌리니.. 잘 됩니다.
void __fastcall TForm1::Button1Click(TObject *Sender)
{
#define NTHREADS 1
HANDLE hThreads[NTHREADS];
int i;
unsigned threadId;
SECURITY_ATTRIBUTES sa = {
sizeof(SECURITY_ATTRIBUTES), /* structure size */
0, /* No security descriptor */
TRUE /* Thread handle is inheritable */
};
//---------------------------------------------------------------------------
stConv *pData192 = new stConv;
pData192->hMain = this->Handle;
pData192->asInFile = edWMAFile->Text; //"D:\\WMA_MP3\\3DM00137205.wma"; //"D:\\CurProject\\CBuilder\\FTP\\Temp\\0de4ec607098343e5bc3122dbcf10edb.mp3";
pData192->asOutFile = edMP3File->Text; //"D:\\WMA_MP3\\3DM00137205_128.mp3"; //D:\\files\\bank97\\musics\\0de4ec607098343e5bc3122dbcf10edb.mp3";
pData192->dwBitrate = 128;
pData192->dwSampleRate = 44100;
hThreads[0] = (HANDLE)_beginthreadex(
&sa, /* Thread security */
10240, /* Thread stack size */
FuncThreadConv, /* Thread starting address */
(void *)pData192, /* Thread start argument */
CREATE_SUSPENDED, /* Create in suspended state */
&threadId); /* Thread ID */
ResumeThread(hThreads[0]);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
if( BASS_Init(0,44100,0,Application->Handle,NULL)==false ) //변환 프로그램은 사운드카드가 필요없다. 그래서 이렇게 했는데... 서버에는 Remote Desktop Protocol (RDP) 잡혀 있다. 이것은 어떤 가상 드라이버로 보여진다. BASS에서는 이것을 지원하지 않는 것으로 보인다.
{
MessageBox( this->Handle, "Can''t initialize device", "Warning", MB_OK );
::PostMessage( this->Handle, WM_CLOSE, 0, 0 );
return;
}
//--------------------------------------------------------------------------
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCloseQuery(TObject *Sender, bool &CanClose)
{
BASS_Free();
}
//---------------------------------------------------------------------------
|