簡易プレイヤ 致命的ミス

簡易プレイヤにはSSE2版とAVX2版がありますが、mp3とm4aのライブラリのリンクが両方ともavx2用でした。

Windowsタブレットで動作させようとしたときに発覚しまして、修正済みです。

ファイルの中身更新してありますので、SSE2の方はダウンロードお願いします。

Vectorへは申請しておきます。

簡易プレイヤその2

alac対応出来ました!

Kb Media Player作者のKobarinさん多謝。

少し変数と受け渡し値が間違っていたようです。
新仕様と旧仕様を見比べて分かりました。

こちらで16bit、24bit、32bitは確認とれました。(シークも問題ないようです)
一応64bitまで対応はしてますが、64bitのalac作ってない物でわかりません。

https://ppp.oohara.jp/ogg.html

追記

今度はflacのシークで問題が。。。。
16bitのflacは正常にシーク出来るのに、24bitのflacでシーク出来ない状態になってます。
何か渡すのがおかしいんだと思うのですが、現在調査中

2019.01.13 11:58 新しいものに入れ替えを行いました。
m4a部分の修正と、flacの調査(24bitのシークのみ出来ません、再生は問題無く出来ます)

https://ppp.oohara.jp/ogg.html

追記2

前バージョンに戻してもだめなので、VS2015を再インストールしてみる。
VCしか使わないから不要な物は入れないことにする。
実は最近VC2015がおかしくなって再インストールしたのでそのせいかもしれないので、念のため。

追記3
VS2015アンインストールしたらインストールできなくなった。

原因をgoogle先生で10時間くらい格闘したけど、治らなかったのでVS2017入れ始めてる。

追記4
VS2017も同じエラーとなった。
どうやら、どっかおかしいっぽい。
10時間くらい調べて、なんとかなりましたので書いておきます。
問題のあるパッケージをlogから見つけます。ここではVC_MFC.source.msiで説明します。
そのままダブルクリックすると、

to install this product please run setup.exe. for other installation options"

と出ますので、
msiexec命令を使って、

msiexec /i VC_MFC.source.msi ADDEPLOY=1

として、問題のパッケージの競合を強引に消していきます。
消せたら普通にインストール出来ます。

簡易プレイヤ

ys8に対応したのはいいのですが、久々に普通のoggを再生したところ、途中で止まって強制終了指定しまいました。

原因つきとめるのにかなり掛かりましたが修正したものを置きました。

まだalacには対応出来てません。
-50エラーだすので、なかなか理由わかんないし。

追記 2019.01.11
-50エラーは取れました。
単に、受け取るバッファサイズが大きかったようです。4096くらいにしたら取れました。
m_pAlacDecoder->mConfig.frameLength

ただ、再生すると、少しのノイズが乗り、
44100Hz/16bitのalacだとテンポが若干速くなり、ノイズが載るので、テンポさえなんとかすればたぶん正常になりそう。
192000Hz/24bitのalacだとテンポめちゃくちゃ遅くなり、ノイズがかなり載るので、
まだ設定忘れているところ有りそう。

元ソースはkb media pleyerの新仕様のkpiソース全部を使っています。

alac側

BOOL __fastcall KbAlacDecoder::Open(const _TCHAR *cszFileName, SOUNDINFO *pInfo)
{
	ZeroMemory(pInfo, sizeof(SOUNDINFO));
	FILE *fp;
#if UNICODE	
	fp = _wfopen(cszFileName, L"rb");
#else
	fp = fopen(cszFileName, _T("rb"));
#endif	
	if (!fp) {
		return FALSE;
	}

	buffer = NULL;
	buffer_size=0;


	m_callback.user_data = fp;
	m_pMp4ff = mp4ff_open_read(&m_callback);
	if (!m_pMp4ff) {
		m_callback.user_data = NULL;
		return 0;
	}
	if (mp4ff_get_decoder_config(m_pMp4ff, 0, &buffer, &buffer_size) != 0) {
		mp4ff_close(m_pMp4ff);
		m_callback.user_data = NULL;
		return 0;
	}
	m_pAlacDecoder = new ALACDecoder;
	if (m_pAlacDecoder->Init(buffer, buffer_size) != 0) {
		delete m_pAlacDecoder;
		free(buffer);
		mp4ff_close(m_pMp4ff);
		m_callback.user_data = NULL;
		return 0;
	}
	free(buffer);
	m_qwLastSample = mp4ff_get_track_duration(m_pMp4ff, 0);
	m_MediaInfo.dwSampleRate = m_pAlacDecoder->mConfig.sampleRate;
	m_MediaInfo.dwChannels = m_pAlacDecoder->mConfig.numChannels;
	m_MediaInfo.nBitsPerSample = m_pAlacDecoder->mConfig.bitDepth;
	m_MediaInfo.qwLength = kpi_SampleTo100ns(m_qwLastSample, m_MediaInfo.dwSampleRate);
	m_MediaInfo.dwSeekableFlags = KPI_MEDIAINFO::SEEK_FLAGS_SAMPLE;
	m_MediaInfo.dwUnitSample = m_pAlacDecoder->mConfig.frameLength;
	m_MediaInfo.dwCount = 1;
	m_MediaInfo.dwNumber = 1;

	pInfo->dwSamplesPerSec = m_pAlacDecoder->mConfig.sampleRate;
	pInfo->dwChannels = m_pAlacDecoder->mConfig.numChannels;
	pInfo->dwBitsPerSample = m_pAlacDecoder->mConfig.bitDepth;
	pInfo->dwLength = kpi_SampleTo100ns(m_qwLastSample, m_MediaInfo.dwSampleRate);
	pInfo->dwSeekable = 1;
	pInfo->dwUnitRender = (m_pAlacDecoder->mConfig.frameLength) / 2;
	
	m_nTrack = 0;
	m_nNumSampleId = mp4ff_num_samples(m_pMp4ff, 0);
	buffer = NULL;
	buffer_size = 0;
	m_BitBuffer.cur = NULL;
	return 1;
}
//////////////////////////////////////////////////////////////////////////////
void __fastcall KbAlacDecoder::Close(void)
{
	delete m_pAlacDecoder; m_pAlacDecoder = NULL;
	if (m_pMp4ff) {
		mp4ff_close(m_pMp4ff);
		m_pMp4ff = NULL;
	}

	if (m_callback.user_data) {
		FILE *fp = (FILE*)m_callback.user_data;
		fclose(fp);
		m_callback.user_data = NULL;
	}
	m_pSample = NULL;
	m_dwLastSample = 0;
	m_dwCurrentSample = 0;
	m_dwRemain = 0;
	m_dwSampleRate = 0;
	m_dwChannels = 0;
	m_nNumSampleId = 0;
	m_nCurrentSampleId = 0;
	m_nTrack = 0;

	m_callback.user_data = NULL;
	m_nNumSampleId = 0;
	m_nCurrentSampleId = 0;
	m_nTrack = 0;
}
//////////////////////////////////////////////////////////////////////////
DWORD __fastcall KbAlacDecoder::Render(BYTE *pBuffer, DWORD dwSizeSample)
{
	DWORD dwRetSample = 0;
	if (m_nCurrentSampleId < m_nNumSampleId) {
		BYTE *buffer = NULL;
		UINT buffer_size = 0;
		int rc = mp4ff_read_sample(m_pMp4ff, m_nTrack, m_nCurrentSampleId, &buffer, &buffer_size);
		m_nCurrentSampleId++;
		BitBufferInit(&m_BitBuffer, buffer, buffer_size);
	}
	return dwRetSample;
}

DWORD __fastcall KbAlacDecoder::Render2(BYTE *pBuffer, DWORD dwSizeSample, DWORD cnt)
{
	DWORD dwRetSample = 0;
	if (m_nCurrentSampleId < m_nNumSampleId) {
		DWORD dwSample = 0;
		//BitBufferInit(&m_BitBuffer, buffer, buffer_size);
		int ret = m_pAlacDecoder->Decode(&m_BitBuffer, pBuffer+cnt, dwSizeSample, m_MediaInfo.dwChannels, (uint32_t*)&dwSample);
		dwRetSample = dwSample;
	}
	return dwRetSample;
}


int __fastcall KbAlacDecoder::SetPosition(DWORD dwPos)
{
	if (dwPos == 0 && m_nCurrentSampleId == 0) {//既に先頭位置にある
												//なくても問題ないが、無駄なので
		return 0;
	}
	int64_t i64PosSample = MulDiv(dwPos, m_dwSampleRate, 1000);
	//↑目的の位置をサンプル単位にしたもの

	int nStartId = 0;
	int nEndId = m_nNumSampleId;
	int64_t i64CurPos;
	while (1) {
		//2分探索で目的の位置を探す(一発で見つける方法が分からないので)
		//mp4ff_find_sample を使えば一発で行けそうだけど使い方が分からない
		int nId = nStartId + (nEndId - nStartId) / 2;
		i64CurPos = mp4ff_get_sample_position(m_pMp4ff, m_nTrack, nId);
		if (i64CurPos == i64PosSample) {//滅多に成立しない
			nStartId = nEndId = nId;
		}
		else if (i64CurPos > i64PosSample) {
			nEndId = nId - 1;
		}
		else {
			nStartId = nId + 1;
		}
		if (nEndId <= nStartId) {
			break;
		}
	}
	m_nCurrentSampleId = nEndId - 1;//1つ手前にシークする
	if (m_nCurrentSampleId >= m_nNumSampleId) {
		m_nCurrentSampleId = m_nNumSampleId - 1;
	}
	if (m_nCurrentSampleId < 0) {
		m_nCurrentSampleId = 0;
	}
	m_pSample = NULL;
	m_dwRemain = 0;
	//足りない分を最大1秒だけ空読み
	//KbMedia Player 本体が必要に応じてやるので実際は不要
	m_dwCurrentSample = mp4ff_get_sample_position(m_pMp4ff, m_nTrack, m_nCurrentSampleId);
	DWORD dwCurPosMS = MulDiv(m_dwCurrentSample, 1000, m_dwSampleRate);
	DWORD dwDeltaMS = dwPos - dwCurPosMS;
	if (dwDeltaMS > 1000) {
		dwDeltaMS = 1000;
	}
	DWORD dwDeltaSample = MulDiv(dwDeltaMS, m_dwSampleRate, 1000);
	DWORD dwDeltaBytes = dwDeltaSample * 2 * m_dwChannels;
	if (dwDeltaBytes) {
		BYTE *pBuffer = (BYTE*)malloc(dwDeltaBytes);
		dwDeltaBytes = Render(pBuffer, dwDeltaBytes);
		dwDeltaMS = MulDiv(dwDeltaBytes / 2 / m_dwChannels, 1000, m_dwSampleRate);
		dwPos = dwCurPosMS + dwDeltaMS;
		free(pBuffer);
	}
	return dwPos;
}

//MP4ファイル読み込みコールバック関数
uint32_t _read_callback(void *user_data, void *buffer, uint32_t length)
{
	return ((CFile*)user_data)->Read(buffer, length);
}

//MP4ファイルシークコールバック関数
uint32_t _seek_callback(void *user_data, uint64_t position)
{
	return (uint32_t)((CFile*)user_data)->Seek(position, CFile::begin);
}

class m4a
{
public:
	static HKMP WINAPI Open(const _TCHAR *cszFileName, SOUNDINFO *pInfo)
	{//MP4 コンテナに格納されていれば KbMp4AacDecoder
	 //MP4 コンテナに格納されなければ KbAacDecoder
	 //を作成し、そのポインタを返す
		FILE *fp;
#if UNICODE	
		fp = _wfopen(cszFileName, _T("rb"));
#else
		fp = fopen(cszFileName, _T("rb"));
#endif	
		if (!fp) {
			return FALSE;
		}

		IKbAacDecoder *pAAC;
		mp4ff_callback_t callback = { 0 };
		callback.read = read_callback;
		callback.seek = seek_callback;
		callback.user_data = fp;
		mp4ff_t *pMp4ff = mp4ff_open_read(&callback);
		if (!pMp4ff) {
			KbAacDecoder *pAacDecoder = new KbAacDecoder;
			DWORD dwCount = pAacDecoder->Open(cszFileName, pInfo);
			if (dwCount) {
				pAAC = pAacDecoder;
				return pAAC;
			}
			else {
				pAacDecoder->Close();
			}
		}
		else
		 {
			if (mp4ff_total_tracks(pMp4ff) > 0) {
				fseek(fp,0, SEEK_SET);
				char a[0x400];
				char b;
				CString str;
				int flag = 0;
				fread(a,0x400,1,fp);
				//if (strstr(a,"alac")==0) {
				//	flag = 1;
				//}
				//if (flag == 0){ //TRACK_AUDIO){
				//	pAAC = new KbMp4AacDecoder;
				//}
				//else if (flag == 1) {//TRACK_AUDIO_ALAC
				//	pAAC = new KbAlacDecoder;
				//}
				if (mp4ff_total_tracks(pMp4ff) > 0) {
					int type = mp4ff_get_track_type(pMp4ff, 0);
					if (type == 1) {//TRACK_AUDIO)
						pAAC = new KbMp4AacDecoder;
					}
					else if (type == 4) {//TRACK_AUDIO_ALAC
						pAAC = new KbAlacDecoder;
					}
				}
			}
		}
		fclose(fp);

		if (pAAC->Open(cszFileName, pInfo)) {
			return pAAC;
		}
		delete pAAC;
		return NULL;
	}

	static void WINAPI Close(HKMP hKMP)
	{
		if (hKMP) {
			IKbAacDecoder *pAAC = (IKbAacDecoder*)hKMP;
			delete pAAC;
			hKMP = NULL;
		}
	}

	static DWORD WINAPI Render(HKMP hKMP, BYTE* Buffer, DWORD dwSize)
	{
		if (!hKMP)return 0;
		IKbAacDecoder *pAAC = (IKbAacDecoder*)hKMP;
		return pAAC->Render(Buffer, dwSize);
	}

	static DWORD WINAPI Render2(HKMP hKMP, BYTE* Buffer, DWORD dwSize,DWORD cnt3)
	{
		if (!hKMP)return 0;
		IKbAacDecoder *pAAC = (IKbAacDecoder*)hKMP;
		return pAAC->Render2(Buffer, dwSize,cnt3);
	}

	static BYTE* WINAPI Buf(HKMP hKMP)
	{
		if (!hKMP)return 0;
		IKbAacDecoder *pAAC = (IKbAacDecoder*)hKMP;
		return pAAC->buffer;
	}

	static DWORD WINAPI SetPosition(HKMP hKMP, DWORD dwPos)
	{
		if (!hKMP)return 0;
		IKbAacDecoder *pAAC = (IKbAacDecoder*)hKMP;
		return pAAC->SetPosition(dwPos);
	}
};

読み込み側

int playwavm4a(BYTE* bw, int old, int l1, int l2)
{
	//データ読み込み
	int rrr = readm4a(bw + old, l1);
	playb += (l1 + l2) / ((wavch == 1 || wavch == 2) ? 4 : (wavch * 2));
//	if (oggsize / ((wavch == 1) ? 1 : 1) - 44100 < = playb * 4) {
	//	if (savedata.saveloop == FALSE) {
		//	l1 = rrr;  fade1 = 1;
//			return l1;
	//	}
	//}
	if (l1 != rrr) {
		if (endf == 1) {
			l1 = rrr; fade1 = 1;
		}
		else {
			loopcnt++;
			playb = loop1;
			m4a_.SetPosition(og->kmp, 0);
			readm4a(bw + old + rrr, l1 - rrr);
		}
	}
	if (l2) {
		rrr = readm4a(bw, l2);
		if (l2 != rrr) {
			if (endf == 1) {
				l2 = rrr; fade1 = 1;
			}
			else {
				loopcnt++;
				playb = loop1;
				m4a_.SetPosition(og->kmp, 0);
				readm4a(bw + rrr, (int)l2 - rrr);
			}
		}
	}
	return l1 + l2;
}

int readm4a(BYTE*bw, int cnt)
{
	_set_se_translator(trans_func);
	DWORD cnt1 = og->sikpi.dwUnitRender * 2, cnt2 = (DWORD)cnt, cnt4; if (cnt1 == 0) cnt1 = 1024;
	m4a_.Render(og->kmp, (BYTE*)bufkpi, cnt1);
	DWORD r = 0;
	 {
		for (;;) {
			if (cnt2 < = cnt3) { r = 1; break; }
			r = m4a_.Render2(og->kmp, (BYTE*)bufkpi, cnt1,cnt3);
			if (r == 0) break;
			cnt3 += r;
		}
		if (m4a_.Buf(og->kmp) != NULL) {
			free(m4a_.Buf(og->kmp));
		}
		cnt4 = cnt3;
		if (r == 0) cnt = 0;
		memcpy(bufkpi2, bufkpi, cnt);
		unsigned short *bf1, *bf2; bf1 = (unsigned short*)bw; bf2 = (unsigned short*)bufkpi2;
		//		int fw = playb % (wavch);
		//		bf2 += fw;
		int cnt1 = cnt / 2;
		switch (wavch)
		{
		case 1:
		case 2:
			memcpy(bw, bufkpi, cnt);
			break;
		case 3: // 2.1   
			for (int sample = 0; sample < cnt1; sample += wavch)
			{
				int ChannelMap[3] = { 2,3,1 };
				for (int ch = 0; ch < wavch; ch++)
				{
					*bf1++ = bf2[ChannelMap[ch] - 1];
				}
				bf2 += wavch;
			}
			break;
		case 4: // Quad   
			for (int sample = 0; sample < cnt1; sample += wavch)
			{
				int ChannelMap[4] = { 2,3,1,4 };
				for (int ch = 0; ch < wavch; ch++)
				{
					*bf1++ = bf2[ChannelMap[ch] - 1];
				}
				bf2 += wavch;
			}
			break;
		case 5: // Surround   
			for (int sample = 0; sample < cnt1; sample += wavch)
			{
				int ChannelMap[5] = { 2,3,1,4,5 };
				for (int ch = 0; ch < wavch; ch++)
				{
					*bf1++ = bf2[ChannelMap[ch] - 1];
				}
				bf2 += wavch;
			}
			break;
		case 6: // 5.1   
			for (int sample = 0; sample < cnt1; sample += wavch)
			{
				int ChannelMap[6] = { 2,3,1,6,4,5 };
				for (int ch = 0; ch < wavch; ch++)
				{
					*bf1++ = bf2[ChannelMap[ch] - 1];
				}
				bf2 += wavch;
			}
			break;
		}
		if (cnt2 <= cnt3) {
			cnt3 -= cnt2;
			if (cnt3 != 0)	memcpy(bufkpi, bufkpi + cnt2, cnt3);
		}
		Int24 *b24c;
		b24c = (Int24*)bw;
		short *b, c;
		b = (short*)bw;
		if (wavsam == 24) {
			for (int i = 0; i < cnt / 3; i++) {
				int c4 = b24c[i];
				c4 = (int)((float)c4 * ((float)savedata.kakuVal / 100.0f));
				b24c[i] = c4;
			}
		}
		else {
			for (int i = 0; i < cnt / 2; i++) {
				int c = (int)b[i];
				c = (int)((float)c * ((float)savedata.kakuVal / 100.0f));
				b[i] = (short)c;
			}
		}
		if (wavsam == 24) {
			for (int i = 0; i < cnt / 3; i++) {
				int c4 = b24c[i];
				if (savedata.mp3 == 2)	c4 = (int)((float)c4 * 2.0f);
				else if (savedata.mp3 == 4) c4 = (int)((float)c4 * 3.0f);
				else if (savedata.mp3 == 8) c4 = (int)((float)c4 * 4.0f);
				else if (savedata.mp3 == 16) c4 = (int)((float)c4 * 5.0f);
				if (c4 >  8388607)c4 = 8388607;
				if (c4 < -8388608)c4 = -8388608;
				b24c[i] = c4;
			}
		}
		else {
			for (int i = 0; i < cnt / 2; i++) {
				int c = (int)b[i];
				if (savedata.mp3 == 2)	c = (int)((float)b[i] * 1.5f);
				else if (savedata.mp3 == 3) c = (int)((float)b[i] * 2.0f);
				else if (savedata.mp3 == 4) c = (int)((float)b[i] * 2.5f);
				else if (savedata.mp3 == 5) c = (int)((float)b[i] * 3.0f);
				if (c >= 32768)c = 32767;
				if (c <= -32767)c = -32766;
				b[i] = (short)c;
			}
		}
		fade += fadeadd; if (fade&lt;0.0001) { fade = 0.0; fadeadd = 0; }
		//fadeを三乗して計算密度を変更
		if (wavsam == 24) {
			float c4;
			int c5;
			for (int i = 0; i < cnt / 3; i++) {
				c5 = b24c[i]; c4 = (float)c5;
				c4 = c4 * fade * fade; c5 = (int)c4;
				b24c[i] = c5;
			}
		}
		else {
			for (int i = 0; i < cnt / 2; i++) { c = b[i]; c = (short)(((float)c) * fade * fade); b[i] = c; }
		}
		if ((UINT)wl<(UINT)0x7fff0000) {
			if (cc1 == 1)	cc.Write(bw, cnt);
			wl += cnt;
		}
		lenl += cnt;
	}
	if (cnt4<cnt) cnt = cnt4;
	return cnt;
}

分かる方いましたら、返信かchacha@oohara.jpへメール下さいませm(__)m

簡易プレイヤ

SteamのYs8のoggに対応できました。
近々公開します。

まず、ys8のoggのバイナリを見て観ましょう。

これだけでは、何がどうなっているのか分からないですよね。
私もこれが何なのかoggのヘッダ部分取っ払ってデータ部分だけなのか、解析する上で全然分からず2,3日掛かりました。

で、ずっと解析を続ける中で先頭部分に注目しました。

これがys8のoggの先頭部分。


これが普通のoggの先頭部分。

なんかパターンみたいなものが見えてきました。

なにか暗号化されているみたいに見えますね。
Address 00 が 04になってます。実際は4Fです。
差分とか色々行うと以下のようになります。
まず、下位4ビットと上位4ビットが入れ替わります。
0x0fとxorを取ります。
04⇒40⇒0x0fとxor⇒4f
ぴったりですね。

他のSteamはパック化されてoggみたいに外に出てないので無理がありますが、Ys8は出来ました。
実装の際、デコードされたogg、ファイル名_dec.oggとして作成します。
内部で処理行ってメモリに置くことも考えたのですが、ファイルとして外に出して、それを再生する形を取りました。

https://ppp.oohara.jp/ogg.html
更新日付変わってませんがファイルは新しくなってます。

m4aのalacは再生しようとすると落ちますのでドロップしないように。(実装中ではありますが)

簡易プレイヤ更新

本日、簡易プレイヤを更新しました。
午前4時に修正版を出したのですが、バグが見つかったため20時過ぎに再度アップしてます。

https://ppp.oohara.jp/ogg.html

内容としては、hes(PCエンジン)のファイルに付随するm3uの解析にてプレイリストに名称をいれる
です。
今回試したのは、風の伝説ザナドゥのhesで、これにはmp3もあります。
よって、m3uの中身はmp3とhesファイル2種類有る形になるため、リストにもmp3とm3u両方載るようにしました。
日本語は確認してませんが、KSSの時と同じソース使ってますので、たぶん行けると思います。

kpiの方は、新仕様のkpiには未対応です。
細かい情報があまりにもなさ過ぎて実装に至ってないって感じです。
よって旧仕様 kbmediaplyer 2.xのものは動きます。
新仕様のものは置いてあっても勝手にスルーされます。(見に行く関数がkpi内にないため)

追記

作者へ問い合わせたところ、実装方法を簡単にですが教えて貰えました。
もしかしたら新仕様のkpiに対応出来るかも知れませんので期待せずまっててね♪

りすてぃんぐ更新

先日、りすてぃんぐを更新しました。
変更内容というか修正内容は、多大な数のサブフォルダがあると処理中に落ちてしまうというものの修正です。
200個とかそれくらいのサブフォルダ数ならば問題はありませんが、6000個とかのサブフォルダが有る場合、メモリ不足(コミットメモリの不足)により落ちました。
単純に再帰的処理で、かなりのメモリを消費していたため、その処理を見直し、再帰処理内のメモリを限りなく削りました。
こちらで発覚した問題ですが、6000個ちょいのフォルダと30000ファイル以上の構成で正常に出力されました。

りすてぃんぐ♪ Ver 0.62s 2018.08.13 20:00

もしくはupdateにて更新してください。

簡易プレイヤ バグ修正

2つほどバグがありましたので修正を行いました。
・flac,m4aでループ再生されない問題を修正。
これはそのままでflac、m4aのループ処理が抜けてました。

・動画再生で音量が反映されない問題を修正。
これは、以前LAVに対応させるため、フィルターをピンコネクト方式を止めた時に要らないところを消した時、消しすぎたみたいです。

ダウンロード(Ver 0.8c 32bit SSE2版 UNICODE版 VS2015ビルド)
2018.06.29
ダウンロード(Ver 0.8c 32bit AVX2版 UNICODE版 VS2015ビルド)
2018.06.29

簡易プレイヤ AVX2版 公開

簡易プレイヤのAVX2版を公開しました。
AVX2の載っているCPU(Intel i7 4000番台以降/AMD Excavatorアーキテクチャ以降)で動きます。
SSE2版は比較的古いPCでも動作するのに対し、AVX2版は近年のCPUでしか動作しません。
ライブラリの類いからすべてAVX2有りでビルドし直し、exeを作りました。
少々サイズは大きくなりましたが、(おそらく)動作は速くなっていると思います。
元々そこまで高負荷なものでもないので、実感できないので正直なところ公開して意味あるのかというのは自分の中にあったりしましたが・・・。
Intel C++とかだともっと高速になるんでしょうけど、めちゃ高いもんねぇ。
VC2015 Communityのcl.exeもそこそこの速度は出るので、そこまで最適化しなくてもいいかなぁと思っています。

ダウンロード(Ver 0.8c 32bit SSE2版 UNICODE版 VS2015ビルド)
2018.05.09
ダウンロード(Ver 0.8c 32bit AVX2版 UNICODE版 VS2015ビルド)
2018.05.16

ことりさんしすてむ

VC2017にてリビルドしてみました。
特にVC2005の状態でも不都合なかったんですが、コア部分(cl.exe)がVC2005から2017で進化しているのではないかという期待でリビルドして運用しています。
あとはMFC側に変更等があって色々修正されているかも!?という期待もこめてますね。

些細なバグはまだまだありますし、ネットワークも安定は完全に出来ていない状態ではありますが、ソース上では特に不都合な部分がなかったので、VC2017でリビルドして様子を見てみようかと。

ちなみに公開はしてません。
ほしいよーって人いたらexeは渡せます。
暫定的にVC2017でリビルドしただけなので、、、

C1900に悩み5時間

興味本位で行ったことが大事故に。
Visual Studio 2017 Communityをダウンロードし、インストールしてみました。
自作の簡易プレイヤをBuildしたところ、各libが古いと言われたので仕方なくすべてのlibファイルを再Build。
無事exeが出来上がったので、実行。
mp3、flacの再生に異常なし。
これはいけるかなと思ったらoggファイルが再生できない。(強制終了する)
ソース同じだしなんだろうとデバッグモードで実行。
再生出来た・・・。
わかんねー。
リリースビルドでデバッグ情報つけて動作させてみたところ、変数の値が途中からおかしくなっていることが発覚。
メモリ違反によりおちていることがわかった。
しかし、解決策が全く分からない。
ogg関連のlibに問題があるのか、ソースに問題があるのか、VC2017自体に問題があるのか。
仕方なくVS2017をアンインストール。
VS2015でビルドし直したところ、C1900のエラーがでた。
恐らくVS2017の残骸だと思い、2時間半くらい掛けて、VS2015を修復。
苦労したで・・・。

同じソースで作ってなぜ2015で動いて2017で動かないのか。

とりあえずVS2017は少し様子見。

簡易プレイヤにLAV対応・・・できず が、できた

LAVを対応しようと奮闘してましたがうまくいかないです。
たぶん普通に再生するだけならなんとかなりそうなのですが、音声ストリームを切り替える部分がどうも実装方法がわからず。

分かる方今したらコメントかメール頂けると嬉しいです。(サンプルがあると助かります VC++)」

追記
GraphStudioNextにて色々いじってたら、IAMStreamSelectインターフェイスでなんとなくできるのかなぁと。
DirectShowのフィルター一覧を検索して LAV Spliter Sourceと見つけたらAudio側のstream番号を変更すれば複数ストリームも対応出来そうな気がします。
今は、Haali + ffdshowを基本フィルタ一覧から拾ってきて手動でPINを繋げてますが、そこにLAVも拾うようにして、設定からLAVを使うのかHaaliを使うのかを選択できるようにして、LAVだったら、stream番号変更の形を取るといけるのかなぁと思ってますがやってみないとなんともですね。
googleで色々調べても何も出てこない(プレイヤ側は出てくるけど、実装側のソースは出てこない)ので暇見てやるしかないね。
自動でPINを組む形(Haaliを使わない+ffdshowを使わない)状態だとH.265再生できるので、Haali Simple Media SplitterにてVIDEOのPINがないことが問題で再生できないっぽいんですよね。

追記

IAMStreamSelectに対応出来ました。

実装方法等は別途書くかも知れません。

追記

メモリ解放わすれてました。

DWORD CntPin2(IAMStreamSelect *pFilter) {
    DWORD i,j,k,k1,k2,l;
    k =k1=k2= 0;
    l = 0;
    au = etc = 0;
    AM_MEDIA_TYPE *am;
    LPWSTR p;
    pFilter->Count(&i);
    l = i;
    for (j = 0; j < i; j++) {
        pFilter->Info(j, &am, NULL, NULL, NULL, &p, NULL, NULL);
        if (am->majortype == MEDIATYPE_Audio) {
            streamname[k] = p;
            k++;
        }
        else {
            l--;
            if (am->majortype == MEDIATYPE_Video) {
                streamname1[k1] = p;
                au++;
                k1++;
            }
            else {
                if (etc == 0)etc = j;
                    streamname2[k2] = p;
                k2++;
            }
        }
        CoTaskMemFree(p);
        DeleteMediaType(am);
        FreeMediaType(*am);
    }
    return l;
}

追記 2017.09.24

VRのみに対応わすれてました(ダイナソアのOPなど)。

if(prend)  // prendはEVRが使えるかのポインタ NULLならEVRは未獲得
    pGraphBuilder->AddFilter(prend, L"Enhanced Video Renderer");
pGraphBuilder->RenderFile(ss,NULL);
//Filtervideooff(pGraphBuilder);
//pGraphBuilder->RenderFile(ss, NULL);
if (prend)
    Filtervideooff2(pGraphBuilder);
//Filtervideooff3(pGraphBuilder);
if(pGraphBuilder)
    pGraphBuilder->QueryInterface(IID_IMediaSeeking,(LPVOID *)&pMediaSeeking);
Filtersdown(pGraphBuilder, NULL);
audionum = 1;
if (iam) {
    audionum = CntPin2(iam);
}

Sound Application 「ささみ」 作成秘話

現在公開している「ささみ」の歴史を時系列でおっていってみようと思います。

まず私の触ったPCとして一番古いのはMSXになります。
その頃はBASICでプログラム組んだりしていましたが、Z80の勉強もしていたのでMSX用のサウンドドライバも作れたかもしれません。
その後PC88になり、Z88は分かっていても、それを編集するソフトがなかったので結局はプログラムをさほど組むこともなくPC98へと行きました。
PC98になり8086にCPUがなりました。
当時専門学校1年でした。
ノートにひたすらニーモニックを書いて、Sound Driverのプログラムを机上で作り上げました。
で、PC98のN88BASIC上のMONコマンドにてインラインアセンブラモードにてSound Driverを完成させました。
それが初めての「ささみ」ということになります。
その後、MS-DOSになって、MASM(macro assembler)6.0にて、MS-DOS版の「ささみ」を作成しました。
ここで苦労したのは常駐でしたね。
一番最初はYM2203(OPN)しか対応していませんでした。
その後、YM2608(OPNA)に対応しましたが、ADPCMには未対応で来ました。
ADPCMは別の方が「ささみ」用に作って下さって、FM音源版は完成しました。

その後、MIDIにも対応したいという考えがおき、MPUにてのMIDI出力に対応しました。
ここまでが、N88BASIC上でのニーモニックオールアセンブラ版となります。

MS-DOSではN88BAISC版を元に、移植を行いました。
そして、RS-MIDI(RS232Cを用いたMIDI)にも対応し、32パートにも対応を行いました。
その後発売されたSuperMPUにはかなり手こずりました。
普通にMPUボードに対し出力を行うと、1ポート(1ch~16ch)と2ポート(17ch~32ch)に同じデータが流れてしまうからです。

ポート分けが大変でした。
当時、Windows95だかのドライバを解析し、何を出力して初期化するのかを徹底的に調べ、SuperMPUに対応したのを覚えています。

その後Windows世代に入るということで「ささみ」もWindows版が必要と感じ、作ったものが現「ささみ」Sound Applicationです。
しかしMIDIのみの対応となりました。
FM音源は出力方法が違うため、対応できず終わりました。
のちのち、CISCさん作のFM音源ジェネレータが出るのですが、「ま、いっか」って感じで対応せず、hootエミュレータ上で「ささみ」を
演奏するという方法でFM音源を実現しました。

文字として書くと短いですが、アセンブラで作るってのはかなり大変でした。

fatal error lnk1104

fatal error lnk1104が突然出るようになった。
原因不明。
連続してビルドすると出ることが多い。
そして、そうなった場合、削除しようとすると、アクセス許可がないとか出て消せない。
5分くらいすると自然に消える。
こうなったのは5/10くらいからだと思う。
いつもどおりビルド、UPXフロントエンドでUPX化・・・しようとすると出る。
違うフロントエンドに代えてみて様子を見ているところ。
VC側のリンクで出来たexeが悪いのかUPXフロントエンドが悪いのかがはっきりしない。
VC2005だったので、VC2010にしてみた。それでも出る。
VC2012以降はWin8用っぽいので購入予定もないです。
Win10がリリースされ、SP1くらいが当たってWin10を購入したら考えると思うけど、
その頃にはVC2015とか出てそうだし。

急に出るようになったので訳分からんわけですが、まぁ1つのexeをUPX化するのに最悪1時間掛ければ出来るのでよしとするしかないのかなぁ。
解決策分かる方情報求む!

5/30追記
英語のサイト見てたら、
Application Experienceサービスが無効になってないか確認するとあったので、確認したら無効になってた。
有効にしたら連続してビルドしても問題が起こらない。
なんだったんだろ・・・。