aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp')
-rw-r--r--Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp265
1 files changed, 265 insertions, 0 deletions
diff --git a/Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp b/Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp
new file mode 100644
index 00000000..bbdda7b7
--- /dev/null
+++ b/Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp
@@ -0,0 +1,265 @@
+#include "ACMEncoder.h"
+
+#define rev32(X) ((((DWORD)(X)&0xFF)<<24)|(((DWORD)(X)&0xFF00)<<8)|(((DWORD)(X)&0xFF0000)>>8)|(((DWORD)(X)&0xFF000000)>>24))
+
+static DWORD FileTell(HANDLE hFile)
+{
+ return SetFilePointer(hFile, 0, 0, FILE_CURRENT);
+}
+static void FileAlign(HANDLE hFile)
+{
+ if (FileTell(hFile)&1) SetFilePointer(hFile, 1, 0, FILE_CURRENT);
+}
+
+#define BUFSIZE 0x20000
+
+ACMEncoder::ACMEncoder(int srate, int nch, int bps, ACMConfig *config)
+{
+ m_did_header = 0;
+ m_srate = srate;
+ m_nch = nch;
+ m_bps = bps;
+ m_error = 0;
+ hStream = 0;
+ hStreamResample = 0;
+ m_acm_resample_buf = NULL;
+ m_acm_resample_outbuf = NULL;
+ m_bytes_done = 0;
+ m_hlen = 0;
+ m_nsam = 0;
+
+ m_acm_buf = (unsigned char *)malloc(BUFSIZE);
+ m_acm_outbuf = (unsigned char *)malloc(BUFSIZE);
+ m_bytes_inbuf = 0;
+ m_bytes_outbuf = 0;
+ m_convert_wfx = config->convert_wfx;
+ do_header = config->header;
+
+ m_wfx_src.wFormatTag = WAVE_FORMAT_PCM;
+ m_wfx_src.nChannels = nch;
+ m_wfx_src.nSamplesPerSec = srate;
+ m_wfx_src.nAvgBytesPerSec = srate * nch * (bps >> 3);
+ m_wfx_src.nBlockAlign = nch * (bps >> 3);
+ m_wfx_src.wBitsPerSample = bps;
+ m_wfx_src.cbSize = 0;
+ MMRESULT rs = acmStreamOpen(&hStream, 0, &m_wfx_src, &m_convert_wfx.wfx, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME);
+ if (rs)
+ {
+ // need resampling
+ WAVEFORMATEX wfx1;
+ ZeroMemory(&wfx1, sizeof(wfx1));
+ wfx1.wFormatTag = WAVE_FORMAT_PCM;
+ if (acmFormatSuggest(0, &m_convert_wfx.wfx, &wfx1, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG)) m_error = 1;
+ else if (acmStreamOpen(&hStream, 0, &wfx1, &m_convert_wfx.wfx, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME)) m_error = 1;
+ else if (acmStreamOpen(&hStreamResample, 0, &m_wfx_src, &wfx1, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME)) m_error = 1;
+ else
+ {
+ ZeroMemory(&ahdResample, sizeof(ahdResample));
+ ahdResample.cbStruct = sizeof(ahdResample);
+ ahdResample.pbSrc = m_acm_resample_buf = (unsigned char *)malloc(BUFSIZE);
+ ahdResample.cbSrcLength = BUFSIZE;
+ ahdResample.pbDst = m_acm_resample_outbuf = (unsigned char *)malloc(BUFSIZE);
+ ahdResample.cbDstLength = BUFSIZE;
+ if (acmStreamPrepareHeader(hStreamResample, &ahdResample, 0)) m_error = 1;
+ m_bytes_inbuf_resample = 0;
+ m_bytes_outbuf_resample = 0;
+ }
+ }
+
+ if (!hStream)
+ {
+ m_error = 1;
+ return ;
+ }
+
+ ZeroMemory(&ahd, sizeof(ahd));
+ ahd.cbStruct = sizeof(ahd);
+ ahd.pbSrc = m_acm_buf;
+ ahd.cbSrcLength = BUFSIZE;
+ ahd.pbDst = m_acm_outbuf;
+ ahd.cbDstLength = BUFSIZE;
+ if (acmStreamPrepareHeader(hStream, &ahd, 0)) m_error = 1;
+}
+
+ACMEncoder::~ACMEncoder()
+{
+ free(m_acm_buf);
+ free(m_acm_outbuf);
+ free(m_acm_resample_buf);
+ free(m_acm_resample_outbuf);
+ if (hStream)
+ {
+ if (ahd.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) acmStreamUnprepareHeader(hStream, &ahd, 0);
+ acmStreamClose(hStream, 0);
+ }
+ if (hStreamResample)
+ {
+ if (ahdResample.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) acmStreamUnprepareHeader(hStreamResample, &ahdResample, 0);
+ acmStreamClose(hStreamResample, 0);
+ }
+}
+
+int ACMEncoder::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail)
+{
+ char *pout = (char *)out;
+ int retval = 0;
+
+ if (!m_did_header && do_header)
+ {
+ int s = 4 + 4 + 12 - 4;
+
+ int t;
+ if (m_convert_wfx.wfx.wFormatTag == WAVE_FORMAT_PCM) t = 0x10;
+ else t = sizeof(WAVEFORMATEX) + m_convert_wfx.wfx.cbSize;
+ s += 4 + t;
+ if (s&1) s++;
+
+ if (m_convert_wfx.wfx.wFormatTag != WAVE_FORMAT_PCM)
+ s += 12;
+
+ s += 8;
+
+ if (out_avail < s) return 0;
+ //xx bytes of randomness
+ m_hlen = s;
+ m_did_header = 1;
+ out_avail -= s;
+ pout += s;
+ retval = s;
+ }
+
+ if (!m_bytes_outbuf)
+ {
+ if (hStreamResample)
+ {
+ if (!m_bytes_outbuf_resample)
+ {
+ DWORD flags = ACM_STREAMCONVERTF_BLOCKALIGN;
+
+ int l = min(in_avail, BUFSIZE - m_bytes_inbuf_resample);
+ if (l < 0) l = 0;
+ if (l > 0) memcpy(m_acm_resample_buf + m_bytes_inbuf_resample, in, l);
+ m_bytes_inbuf_resample += l;
+ *in_used = l;
+ m_nsam += l;
+
+ ahdResample.cbSrcLength = m_bytes_inbuf_resample;
+ acmStreamConvert(hStreamResample, &ahdResample, flags);
+ m_bytes_inbuf_resample -= ahdResample.cbSrcLengthUsed;
+ memcpy(m_acm_resample_buf, m_acm_resample_buf + ahdResample.cbSrcLengthUsed, m_bytes_inbuf_resample); //memmove
+ m_bytes_outbuf_resample = ahdResample.cbDstLengthUsed;
+ }
+ in = (void*)m_acm_resample_outbuf;
+ in_avail = m_bytes_outbuf_resample;
+ m_bytes_outbuf_resample = 0;
+ in_used = NULL;
+ }
+
+ DWORD flags = ACM_STREAMCONVERTF_BLOCKALIGN;
+
+ int l = min(in_avail, BUFSIZE - m_bytes_inbuf);
+ if (l < 0) l = 0;
+ if (l > 0) memcpy(m_acm_buf + m_bytes_inbuf, in, l);
+ m_bytes_inbuf += l;
+ if (in_used)
+ {
+ *in_used = l;
+ m_nsam += l;
+ }
+
+ if (m_bytes_inbuf)
+ {
+ ahd.cbSrcLength = m_bytes_inbuf;
+ acmStreamConvert(hStream, &ahd, flags);
+ m_bytes_inbuf -= ahd.cbSrcLengthUsed;
+ memcpy(m_acm_buf, m_acm_buf + ahd.cbSrcLengthUsed, m_bytes_inbuf); //memmove
+ m_bytes_outbuf = ahd.cbDstLengthUsed;
+ m_bytes_done += l;
+ }
+ }
+ if (m_bytes_outbuf)
+ {
+ int l = min(out_avail, m_bytes_outbuf);
+ memcpy(pout, m_acm_outbuf, l);
+ m_bytes_outbuf -= l;
+ memcpy(m_acm_outbuf, m_acm_outbuf + l, m_bytes_outbuf);
+ retval += l;
+ }
+
+ return retval;
+}
+
+void ACMEncoder::FinishAudio(const wchar_t *filename)
+{
+ if (!do_header) return ;
+
+ HANDLE fh = CreateFileW(filename, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if (fh == INVALID_HANDLE_VALUE)
+ return;
+
+ int len, i;
+ const unsigned char ispred1[4] =
+ {
+ 0x52 , 0x49 , 0x46 , 0x46
+ };
+ const unsigned char ispred2[12] =
+ {
+ 0x57, 0x41 , 0x56 , 0x45 , 0x66 , 0x6d , 0x74 , 0x20 , 0x10 , 0x0 , 0x0 , 0x0
+ };
+
+ len = m_bytes_done;
+ DWORD a = 0;
+
+ FileAlign(fh);
+
+ SetFilePointer(fh, 0, 0, FILE_BEGIN);
+
+ WriteFile(fh, ispred1, sizeof(ispred1), &a, NULL);
+ i = len + (m_hlen) - 8;
+ if (i&1) i++;
+ a = 0; WriteFile(fh, &i, 4, &a, NULL);
+ a = 0; WriteFile(fh, ispred2, sizeof(ispred2) - (hStream ? 4 : 0), &a, NULL);
+
+ int t;
+ if (m_convert_wfx.wfx.wFormatTag == WAVE_FORMAT_PCM) t = 0x10;
+ else t = sizeof(WAVEFORMATEX) + m_convert_wfx.wfx.cbSize;
+ a = 0; WriteFile(fh, &t, 4, &a, 0);
+ a = 0; WriteFile(fh, &m_convert_wfx.wfx, t, &a, 0);
+
+ FileAlign(fh);
+
+ DWORD fact_ofs = 0;
+ if (m_convert_wfx.wfx.wFormatTag != WAVE_FORMAT_PCM)
+ {
+ t = rev32('fact');
+ a = 0; WriteFile(fh, &t, 4, &a, 0);
+ t = 4;
+ a = 0; WriteFile(fh, &t, 4, &a, 0);
+ fact_ofs = FileTell(fh);
+ SetFilePointer(fh, 4, 0, FILE_CURRENT);
+ }
+
+ t = rev32('data');
+ WriteFile(fh, &t, 4, &a, 0);
+ DWORD data_ofs = FileTell(fh);
+
+ {
+ DWORD t, bw = 0;
+ SetFilePointer(fh, 4, 0, FILE_BEGIN);
+ t = GetFileSize(fh, 0) - 8;
+ WriteFile(fh, &t, 4, &bw, 0);
+ DWORD data_size = GetFileSize(fh, 0) - (data_ofs + 4);
+ SetFilePointer(fh, data_ofs, 0, FILE_BEGIN);
+ bw = 0; WriteFile(fh, &data_size, 4, &bw, 0);
+ if (fact_ofs)
+ {
+ SetFilePointer(fh, fact_ofs, 0, FILE_BEGIN);
+ t = m_nsam / ((m_bps >> 3) * m_nch);
+ WriteFile(fh, &t, 4, &bw, 0);
+ }
+ }
+
+ CloseHandle(fh);
+}
+
+int ACMEncoder::GetLastError() { return m_error; } \ No newline at end of file