#include "tsound.h" #ifndef TNZCORE_LIGHT #include "tthread.h" #endif #include "tsop.h" #include #include "tsystem.h" //========================================================= //forward declarations class TSoundOutputDeviceImp; class TSoundInputDeviceImp; //========================================================= namespace { void CALLBACK recordCB( HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2); bool setRecordLine(TSoundInputDevice::Source typeInput); MMRESULT getLineInfo( HMIXEROBJ hMixer, DWORD dwComponentType, MIXERLINE &mxl); MMRESULT getLineControl( MIXERCONTROL &mxc, HMIXEROBJ hMixer, DWORD dwLineID, DWORD dwControlType); MMRESULT setControlDetails( HMIXEROBJ hMixer, DWORD dwSelectControlID, DWORD dwMultipleItems, MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue); MMRESULT getControlDetails( HMIXEROBJ hMixer, DWORD dwSelectControlID, DWORD dwMultipleItems, MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue); MMRESULT isaFormatSupported( int sampleRate, int channelCount, int bitPerSample, bool input); DWORD WINAPI MyWaveOutCallbackThread(LPVOID lpParameter); void getAmplitude(int &litude, const TSoundTrackP st, TINT32 sample); } //============================================================================== // Class to send the message that a playback is completed //============================================================================== #ifndef TNZCORE_LIGHT class EndPlayMsg : public TThread::Message { public: EndPlayMsg(TSoundOutputDeviceListener *notifier) { m_listener = notifier; } TThread::Message *clone() const { return new EndPlayMsg(*this); } void onDeliver() { m_listener->onPlayCompleted(); } private: TSoundOutputDeviceListener *m_listener; }; #endif int makeDWORD(const short lo, const short hi) { int dw = hi << 16; dw |= lo; return dw; } //============================================================================== class WavehdrQueue; class TSoundOutputDeviceImp { public: HWAVEOUT m_wout; WavehdrQueue *m_whdrQueue; TSoundTrackFormat m_currentFormat; std::set m_supportedRate; TThread::Mutex m_mutex; bool m_stopped; bool m_isPlaying; bool m_looped; bool m_scrubbing; std::set m_listeners; DWORD m_notifyThreadId; HANDLE m_closeDevice; TSoundOutputDeviceImp(); ~TSoundOutputDeviceImp(); bool doOpenDevice(const TSoundTrackFormat &format); bool doPlay(WAVEHDR *whdr, const TSoundTrackFormat format); bool doCloseDevice(); bool verifyRate(); void insertAllRate(); }; //============================================================================== class WavehdrQueue { public: WavehdrQueue(TSoundOutputDeviceImp *devImp, int slotCount) : m_devImp(devImp), m_items(), m_queuedItems(), m_slotCount(slotCount), m_mutex(), m_lastOffset(0) { } ~WavehdrQueue() {} void put(TSoundTrackP &subTrack); WAVEHDR *get(); bool popFront(int count); void pushBack(WAVEHDR *whdr, TSoundTrackP st); int size(); void clear(); bool isAllQueuedItemsPlayed(); private: std::list> m_items; std::list m_queuedItems; TThread::Mutex m_mutex; int m_slotCount; int m_lastOffset; TSoundOutputDeviceImp *m_devImp; TSoundTrackP m_lastTrack; }; //============================================================================== WAVEHDR *prepareWaveHeader(HWAVEOUT wout, const TSoundTrackP &subTrack, ULONG &count) { WAVEHDR *whdr = new WAVEHDR; memset(whdr, 0, sizeof(WAVEHDR)); whdr->dwBufferLength = subTrack->getSampleSize() * subTrack->getSampleCount(); whdr->lpData = new char[whdr->dwBufferLength]; whdr->dwFlags = 0; whdr->dwUser = count; memcpy(whdr->lpData, subTrack->getRawData(), whdr->dwBufferLength); MMRESULT ret = waveOutPrepareHeader(wout, whdr, sizeof(WAVEHDR)); if (ret != MMSYSERR_NOERROR) { delete[] whdr->lpData; delete whdr; return 0; } ++count; return whdr; } //============================================================================== void WavehdrQueue::put(TSoundTrackP &subTrack) { assert(subTrack->getRawData()); static ULONG count = 1; //codice messo per tab: facendo il play al rilascio del mouse e su piu' //colonne in cui le traccie potrebbe avere diversi formati siccome qui in //alcune situazioni si fa subito waveOutWrite c'e' bisogno di controllare //se il formato con cui e' stato aperto in precedenza il device e' uguale //a quello della traccia if (m_devImp->m_wout && m_devImp->m_currentFormat != subTrack->getFormat()) { m_devImp->doCloseDevice(); TSystem::sleep(300); m_devImp->doOpenDevice(subTrack->getFormat()); } TThread::MutexLocker sl(&m_mutex); if (!m_devImp->m_scrubbing) { WAVEHDR *whdr2 = 0; //traccia whdr2 = prepareWaveHeader(m_devImp->m_wout, subTrack, count); getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); MMRESULT ret = MMSYSERR_NOERROR; if (whdr2 && whdr2->dwFlags & WHDR_PREPARED) { ret = waveOutWrite(m_devImp->m_wout, whdr2, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { pushBack(whdr2, subTrack); getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); TThread::MutexLocker sl(&m_devImp->m_mutex); m_devImp->m_isPlaying = true; m_devImp->m_stopped = false; } } return; } if (m_queuedItems.size() == 0) { WAVEHDR *whdr1 = 0; WAVEHDR *whdr2 = 0; WAVEHDR *whdr3 = 0; MMRESULT ret; TSoundTrackP riseTrack, decayTrack; int sampleSize = subTrack->getSampleSize(); //cresce riseTrack = TSop::fadeIn(subTrack, 0.9); whdr1 = prepareWaveHeader(m_devImp->m_wout, riseTrack, count); //traccia whdr2 = prepareWaveHeader(m_devImp->m_wout, subTrack, count); getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); //decresce decayTrack = 0; if (m_lastOffset) { decayTrack = TSop::fadeOut(subTrack, 0.9); whdr3 = prepareWaveHeader(m_devImp->m_wout, decayTrack, count); } if (whdr1 && (whdr1->dwFlags & WHDR_PREPARED)) { ret = waveOutWrite(m_devImp->m_wout, whdr1, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { pushBack(whdr1, riseTrack); getAmplitude(m_lastOffset, riseTrack, riseTrack->getSampleCount() - 1L); } } if (whdr2 && (whdr2->dwFlags & WHDR_PREPARED)) { ret = waveOutWrite(m_devImp->m_wout, whdr2, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { pushBack(whdr2, subTrack); getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); TThread::MutexLocker sl(&m_devImp->m_mutex); m_devImp->m_isPlaying = true; m_devImp->m_stopped = false; } } if (whdr3 && (whdr3->dwFlags & WHDR_PREPARED)) { ret = waveOutWrite(m_devImp->m_wout, whdr3, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { pushBack(whdr3, decayTrack); if (decayTrack->isSampleSigned()) m_lastOffset = 0; else m_lastOffset = 127; } } return; } if (m_queuedItems.size() < 10) { WAVEHDR *whdr1 = 0; WAVEHDR *whdr2 = 0; WAVEHDR *whdr = new WAVEHDR; memset(whdr, 0, sizeof(WAVEHDR)); whdr->dwBufferLength = subTrack->getSampleSize() * subTrack->getSampleCount(); whdr->lpData = new char[whdr->dwBufferLength]; whdr->dwFlags = 0; memcpy(whdr->lpData, subTrack->getRawData(), whdr->dwBufferLength); int sampleSize = subTrack->getSampleSize(); TSoundTrackP riseTrack = 0; if (m_lastOffset) ///devo fare ilcross fade { int offset; getAmplitude(offset, subTrack, 0L); offset = m_lastOffset - offset; if (offset) { TSoundTrackP st = TSop::crossFade(m_lastTrack, subTrack, 0.3); memcpy(whdr->lpData, st->getRawData(), st->getSampleCount() * sampleSize); } } else // e' zero ma ne metto uno che cresce faccio il fadeIn { riseTrack = TSop::fadeIn(subTrack, 0.3); whdr1 = prepareWaveHeader(m_devImp->m_wout, riseTrack, count); } whdr->dwUser = count; ++count; MMRESULT ret = waveOutPrepareHeader(m_devImp->m_wout, whdr, sizeof(WAVEHDR)); if (ret != MMSYSERR_NOERROR) { delete[] whdr->lpData; delete whdr; return; } getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); TSoundTrackP decayTrack = 0; if (m_queuedItems.size() <= 7) { if (m_lastOffset) //devo fare il fadeOut { decayTrack = TSop::fadeOut(subTrack, 0.3); whdr2 = prepareWaveHeader(m_devImp->m_wout, decayTrack, count); } } if (whdr1 && whdr1->dwFlags & WHDR_PREPARED) { ret = waveOutWrite(m_devImp->m_wout, whdr1, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { pushBack(whdr1, riseTrack); getAmplitude(m_lastOffset, riseTrack, riseTrack->getSampleCount() - 1L); } } if (whdr && whdr->dwFlags & WHDR_PREPARED) { ret = waveOutWrite(m_devImp->m_wout, whdr, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { pushBack(whdr, subTrack); getAmplitude(m_lastOffset, subTrack, subTrack->getSampleCount() - 1L); { TThread::MutexLocker sl(&m_devImp->m_mutex); m_devImp->m_isPlaying = true; m_devImp->m_stopped = false; } } } if (whdr2 && whdr2->dwFlags & WHDR_PREPARED) { ret = waveOutWrite(m_devImp->m_wout, whdr2, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { pushBack(whdr2, decayTrack); if (decayTrack->isSampleSigned()) m_lastOffset = 0; else m_lastOffset = 127; } } return; } if ((int)m_items.size() == m_slotCount) { WAVEHDR *item = m_items.front().first; MMRESULT ret = waveOutUnprepareHeader( m_devImp->m_wout, item, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { delete[] item->lpData; delete item; } m_items.pop_front(); if (m_queuedItems.size() != 0) { WAVEHDR *item = m_items.front().first; int sampleSize = m_items.front().second->getSampleSize(); int offset; getAmplitude(offset, m_items.front().second, 0L); offset = m_lastOffset - offset; if (offset) { TSoundTrackP st = TSop::crossFade( m_lastTrack, m_items.front().second, 0.3); memcpy(item->lpData, st->getRawData(), st->getSampleCount() * sampleSize); } } } WAVEHDR *whdr = prepareWaveHeader(m_devImp->m_wout, subTrack, count); assert(whdr && whdr->dwFlags & WHDR_PREPARED); m_items.push_back(std::make_pair(whdr, subTrack)); assert((int)m_items.size() <= m_slotCount); } //---------------------------------------------------------------------------- // restituisce il piu' vecchio WAVEHDR il cui stato e' prepared && !done WAVEHDR *WavehdrQueue::get() { TThread::MutexLocker sl(&m_mutex); if (m_items.size() == 0) return 0; WAVEHDR *whdr = m_items.front().first; assert(whdr->dwFlags & WHDR_PREPARED); pushBack(whdr, m_items.front().second); getAmplitude( m_lastOffset, m_items.front().second, m_items.front().second->getSampleCount() - 1L); m_items.pop_front(); return whdr; } //----------------------------------------------------------------------------- // rimuove dalla coda il piu' vecchio WAVEHDR il cui stato e' done bool WavehdrQueue::popFront(int count) { TThread::MutexLocker sl(&m_mutex); //assert(m_queuedItems.size() > 0); if (m_queuedItems.size() <= 0) return false; WAVEHDR *whdr = m_queuedItems.front(); // controllo introdotto pr via che su win2k si perde alcune // notifiche di WHDR_DONE while ((DWORD)count > whdr->dwUser) { MMRESULT ret = waveOutUnprepareHeader( m_devImp->m_wout, whdr, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { m_queuedItems.pop_front(); delete[] whdr->lpData; delete whdr; whdr = m_queuedItems.front(); } } assert(whdr->dwFlags & WHDR_DONE); m_queuedItems.pop_front(); delete[] whdr->lpData; delete whdr; return true; } //----------------------------------------------------------------------------- void WavehdrQueue::pushBack(WAVEHDR *whdr, TSoundTrackP st) { TThread::MutexLocker sl(&m_mutex); m_queuedItems.push_back(whdr); m_lastTrack = st; } //----------------------------------------------------------------------------- int WavehdrQueue::size() { TThread::MutexLocker sl(&m_mutex); int size = m_queuedItems.size(); return size; } //----------------------------------------------------------------------------- void WavehdrQueue::clear() { TThread::MutexLocker sl(&m_mutex); m_items.clear(); m_lastTrack = TSoundTrackP(); std::list::iterator it; for (it = m_queuedItems.begin(); it != m_queuedItems.end(); it++) { WAVEHDR *wvhdr = *it; delete[] wvhdr->lpData; delete wvhdr; } m_queuedItems.clear(); } //----------------------------------------------------------------------------- bool WavehdrQueue::isAllQueuedItemsPlayed() { std::list::iterator it; bool finished = true; for (it = m_queuedItems.begin(); it != m_queuedItems.end(); it++) { WAVEHDR *wvhdr = *it; finished = finished && (wvhdr->dwFlags & WHDR_DONE); } return finished; } //============================================================================== TSoundOutputDeviceImp::TSoundOutputDeviceImp() : m_stopped(true), m_isPlaying(false), m_looped(false), m_scrubbing(false), m_wout(0) { m_whdrQueue = new WavehdrQueue(this, 4); insertAllRate(); if (!verifyRate()) throw TSoundDeviceException( TSoundDeviceException::FailedInit, "Unable to verify supported rates"); m_closeDevice = CreateEvent( NULL, // no security attributes FALSE, // auto-reset event FALSE, // initial state is not signaled NULL); // object not named } //---------------------------------------------------------------------------- TSoundOutputDeviceImp::~TSoundOutputDeviceImp() { delete m_whdrQueue; } //---------------------------------------------------------------------------- bool TSoundOutputDeviceImp::doOpenDevice(const TSoundTrackFormat &format) { WAVEFORMATEX wf; wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = format.m_channelCount; wf.nSamplesPerSec = format.m_sampleRate; wf.wBitsPerSample = format.m_bitPerSample; wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) >> 3; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; wf.cbSize = 0; TThread::MutexLocker sl(&m_mutex); CloseHandle(CreateThread(NULL, 0, MyWaveOutCallbackThread, (LPVOID) this, 0, &m_notifyThreadId)); MMRESULT ret; if ((ret = waveOutOpen(&m_wout, WAVE_MAPPER, &wf, (DWORD)m_notifyThreadId, (DWORD) this, CALLBACK_THREAD)) != MMSYSERR_NOERROR) { while (!PostThreadMessage(m_notifyThreadId, WM_QUIT, 0, 0)) ; } if (ret != MMSYSERR_NOERROR) return false; if (ret != MMSYSERR_NOERROR) return false; m_currentFormat = format; return (ret == MMSYSERR_NOERROR); } //---------------------------------------------------------------------------- bool TSoundOutputDeviceImp::doPlay(WAVEHDR *whdr, const TSoundTrackFormat format) { TThread::MutexLocker sl(&m_mutex); if (!m_wout || (m_wout && m_currentFormat != format)) doOpenDevice(format); MMRESULT ret; ret = waveOutWrite(m_wout, whdr, sizeof(WAVEHDR)); if (ret != MMSYSERR_NOERROR) return false; m_stopped = false; m_isPlaying = true; return true; } //---------------------------------------------------------------------------- bool TSoundOutputDeviceImp::doCloseDevice() { TThread::MutexLocker sl(&m_mutex); if (m_wout) { MMRESULT ret = waveOutReset(m_wout); if (ret != MMSYSERR_NOERROR) return false; ret = waveOutClose(m_wout); if (ret != MMSYSERR_NOERROR) return false; m_wout = 0; m_stopped = true; m_isPlaying = false; m_looped = false; } PostThreadMessage(m_notifyThreadId, WM_QUIT, 0, 0); return true; } //---------------------------------------------------------------------------- void TSoundOutputDeviceImp::insertAllRate() { m_supportedRate.insert(8000); m_supportedRate.insert(11025); m_supportedRate.insert(16000); m_supportedRate.insert(22050); m_supportedRate.insert(32000); m_supportedRate.insert(44100); m_supportedRate.insert(48000); } //---------------------------------------------------------------------------- bool TSoundOutputDeviceImp::verifyRate() { std::set::iterator it; for (it = m_supportedRate.begin(); it != m_supportedRate.end();) { MMRESULT ret; WAVEFORMATEX wf; wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = 1; wf.nSamplesPerSec = *it; wf.wBitsPerSample = 8; wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) >> 3; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; wf.cbSize = 0; ret = waveOutOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); if (ret == MMSYSERR_NOERROR) { ++it; continue; } if (ret == WAVERR_BADFORMAT) it = m_supportedRate.erase(it); else return false; } if (m_supportedRate.end() == m_supportedRate.begin()) return false; return true; } //============================================================================== TSoundOutputDevice::TSoundOutputDevice() : m_imp(new TSoundOutputDeviceImp()) { } //------------------------------------------------------------------------------ TSoundOutputDevice::~TSoundOutputDevice() { close(); WaitForSingleObject(m_imp->m_closeDevice, INFINITE); CloseHandle(m_imp->m_closeDevice); delete m_imp; } //------------------------------------------------------------------------------ namespace { DWORD WINAPI MyWaveOutCallbackThread(LPVOID lpParameter) { TSoundOutputDeviceImp *devImp = (TSoundOutputDeviceImp *)lpParameter; if (!devImp) return 0; MSG msg; BOOL bRet; while (bRet = GetMessage(&msg, NULL, 0, 0)) { if (bRet == -1) { // si e' verificato un errore break; } switch (msg.message) { case MM_WOM_DONE: { WAVEHDR *pWaveHdr = ((WAVEHDR *)msg.lParam); { TThread::MutexLocker sl(&devImp->m_mutex); if (devImp->m_looped) { devImp->doPlay(pWaveHdr, devImp->m_currentFormat); continue; } } WAVEHDR *whdr = 0; if (devImp->m_whdrQueue->popFront(pWaveHdr->dwUser)) { whdr = devImp->m_whdrQueue->get(); if (whdr) devImp->doPlay(whdr, devImp->m_currentFormat); WaitForSingleObject(devImp->m_closeDevice, INFINITE); CloseHandle(devImp->m_closeDevice); MMRESULT ret = waveOutUnprepareHeader( devImp->m_wout, pWaveHdr, sizeof(WAVEHDR)); if (ret == MMSYSERR_NOERROR) { delete pWaveHdr->lpData; delete pWaveHdr; } } if (!whdr && devImp->m_whdrQueue->size() == 0) { std::set::iterator it = devImp->m_listeners.begin(); for (; it != devImp->m_listeners.end(); ++it) { #ifndef TNZCORE_LIGHT EndPlayMsg *event = new EndPlayMsg(*it); event->send(); #else assert(false); #endif } devImp->doCloseDevice(); } continue; } case MM_WOM_CLOSE: break; default: continue; } } SetEvent(devImp->m_closeDevice); return 0; } void getAmplitude(int &litude, const TSoundTrackP st, TINT32 sample) { TSoundTrackP snd = st; amplitude = 0; int k = 0; for (k = 0; k < snd->getChannelCount(); ++k) amplitude += (int)snd->getPressure(sample, k); amplitude /= k; } } //------------------------------------------------------------------------------ bool TSoundOutputDevice::installed() { int ndev = waveOutGetNumDevs(); if (ndev <= 0) return false; return true; } //------------------------------------------------------------------------------ bool TSoundOutputDevice::open(const TSoundTrackP &st) { return m_imp->doOpenDevice(st->getFormat()); } //------------------------------------------------------------------------------ void TSoundOutputDevice::attach(TSoundOutputDeviceListener *listener) { m_imp->m_listeners.insert(listener); } //------------------------------------------------------------------------------ void TSoundOutputDevice::detach(TSoundOutputDeviceListener *listener) { m_imp->m_listeners.erase(listener); } //------------------------------------------------------------------------------ void TSoundOutputDevice::play( const TSoundTrackP &st, TINT32 s0, TINT32 s1, bool loop, bool scrubbing) { assert((scrubbing && !loop) || !scrubbing); if (!st->getSampleCount()) return; TSoundTrackFormat format; TSoundTrackP subTrack; { TThread::MutexLocker sl(&m_imp->m_mutex); if (m_imp->m_looped) throw TSoundDeviceException( TSoundDeviceException::Busy, "Unable to queue another playback when the sound player is looping"); format = st->getFormat(); try { TSoundTrackFormat fmt = getPreferredFormat(format); if (fmt != format) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, "Unsupported Format"); } } catch (TSoundDeviceException &e) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, e.getMessage()); } assert(s1 >= s0); subTrack = st->extract(s0, s1); m_imp->m_looped = loop; m_imp->m_scrubbing = scrubbing; } if (!m_imp->m_wout) m_imp->doOpenDevice(format); m_imp->m_whdrQueue->put(subTrack); } //------------------------------------------------------------------------------ void TSoundOutputDevice::stop() { if ((m_imp->m_wout) && m_imp->m_isPlaying) { MMRESULT ret = waveOutReset(m_imp->m_wout); if (ret != MMSYSERR_NOERROR) return; TThread::MutexLocker sl(&m_imp->m_mutex); m_imp->m_looped = false; m_imp->m_stopped = true; m_imp->m_isPlaying = false; m_imp->m_whdrQueue->clear(); } } //------------------------------------------------------------------------------ bool TSoundOutputDevice::close() { return m_imp->doCloseDevice(); } //------------------------------------------------------------------------------ bool TSoundOutputDevice::isPlaying() const { return m_imp->m_isPlaying; } //------------------------------------------------------------------------------ bool TSoundOutputDevice::isAllQueuedItemsPlayed() { return m_imp->m_whdrQueue->isAllQueuedItemsPlayed(); } //------------------------------------------------------------------------------ bool TSoundOutputDevice::isLooping() { TThread::MutexLocker sl(&m_imp->m_mutex); return m_imp->m_looped; } //------------------------------------------------------------------------------ void TSoundOutputDevice::setLooping(bool loop) { TThread::MutexLocker sl(&m_imp->m_mutex); m_imp->m_looped = loop; } //------------------------------------------------------------------------------ TSoundTrackFormat TSoundOutputDevice::getPreferredFormat( TUINT32 sampleRate, int channelCount, int bitPerSample) { TSoundTrackFormat fmt; //avvvicinarsi al sample rate => dovrebbe esser OK avendo selezionato i piu' vicini std::set::iterator it = m_imp->m_supportedRate.lower_bound(sampleRate); if (it == m_imp->m_supportedRate.end()) { it = std::max_element(m_imp->m_supportedRate.begin(), m_imp->m_supportedRate.end()); if (it != m_imp->m_supportedRate.end()) sampleRate = *(m_imp->m_supportedRate.rbegin()); else throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, "There isn't a supported format"); } else sampleRate = *it; if (bitPerSample <= 8) bitPerSample = 8; else if ((bitPerSample > 8 && bitPerSample < 16) || bitPerSample >= 16) bitPerSample = 16; if (bitPerSample >= 16) fmt.m_signedSample = true; else fmt.m_signedSample = false; //switch mono/stereo if (channelCount <= 1) channelCount = 1; else channelCount = 2; fmt.m_bitPerSample = bitPerSample; fmt.m_channelCount = channelCount; fmt.m_sampleRate = sampleRate; return fmt; } //------------------------------------------------------------------------------ TSoundTrackFormat TSoundOutputDevice::getPreferredFormat(const TSoundTrackFormat &format) { try { return getPreferredFormat( format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); } catch (TSoundDeviceException &e) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, e.getMessage()); } } //============================================================================== //============================================================================== // Classi per la gestione della registrazione //============================================================================== //============================================================================== //============================================================================== class WaveFormat : public WAVEFORMATEX { public: WaveFormat(){}; WaveFormat(unsigned char channelCount, unsigned TINT32 sampleRate, unsigned char bitPerSample); ~WaveFormat(){}; }; WaveFormat::WaveFormat(unsigned char channelCount, unsigned TINT32 sampleRate, unsigned char bitPerSample) { wFormatTag = WAVE_FORMAT_PCM; nChannels = channelCount; nSamplesPerSec = sampleRate; wBitsPerSample = bitPerSample; nBlockAlign = (channelCount * bitPerSample) >> 3; nAvgBytesPerSec = nBlockAlign * sampleRate; cbSize = 0; } //============================================================================== class WinSoundInputDevice { public: WinSoundInputDevice(); ~WinSoundInputDevice(); void open(const WaveFormat &wf); void close(); void prepareHeader(char *sampleBuffer, unsigned TINT32 sampleBufferSize, WAVEHDR &whdr); void unprepareHeader(WAVEHDR &whdr); void addBlock(WAVEHDR &whdr); void start(); void reset(); void stop(); HANDLE m_hBlockDone; private: HWAVEIN m_hWaveIn; }; //-------------------------------------------------------------------- WinSoundInputDevice::WinSoundInputDevice() : m_hWaveIn(0) { m_hBlockDone = CreateEvent( NULL, // no security attributes FALSE, // auto-reset event FALSE, // initial state is not signaled NULL); // object not named } //-------------------------------------------------------------------- WinSoundInputDevice::~WinSoundInputDevice() { CloseHandle(m_hBlockDone); } //-------------------------------------------------------------------- void WinSoundInputDevice::open(const WaveFormat &wf) { if (m_hWaveIn) close(); MMRESULT ret = waveInOpen( &m_hWaveIn, WAVE_MAPPER, &wf, (DWORD)recordCB, (DWORD)m_hBlockDone, CALLBACK_FUNCTION); if (ret != MMSYSERR_NOERROR) { throw TException("Error to open the input device"); } } //-------------------------------------------------------------------- void WinSoundInputDevice::close() { if (!m_hWaveIn) return; MMRESULT ret = waveInClose(m_hWaveIn); if (ret != MMSYSERR_NOERROR) { throw TException("Error to close the input device"); } m_hWaveIn = 0; } //-------------------------------------------------------------------- void WinSoundInputDevice::prepareHeader(char *sampleBuffer, unsigned TINT32 sampleBufferSize, WAVEHDR &whdr) { whdr.lpData = sampleBuffer; whdr.dwBufferLength = sampleBufferSize; // numero di byte whdr.dwFlags = 0; whdr.dwLoops = 0; MMRESULT ret = waveInPrepareHeader(m_hWaveIn, &whdr, sizeof(WAVEHDR)); if (ret != MMSYSERR_NOERROR) { throw TException("Unable to prepare a wave header"); } } //-------------------------------------------------------------------- void WinSoundInputDevice::unprepareHeader(WAVEHDR &whdr) { MMRESULT ret = waveInUnprepareHeader(m_hWaveIn, &whdr, sizeof(WAVEHDR)); if (ret != MMSYSERR_NOERROR) { throw TException("Unable to unprepare a wave header"); } } //-------------------------------------------------------------------- void WinSoundInputDevice::addBlock(WAVEHDR &whdr) { MMRESULT ret = waveInAddBuffer(m_hWaveIn, &whdr, sizeof(WAVEHDR)); if (ret != MMSYSERR_NOERROR) { throw TException("Unable to add a waveheader"); } } //-------------------------------------------------------------------- void WinSoundInputDevice::start() { int ret = waveInStart(m_hWaveIn); if (ret != MMSYSERR_NOERROR) { throw TException("Unable to add a waveheader"); } } //-------------------------------------------------------------------- void WinSoundInputDevice::reset() { if (!m_hWaveIn) return; MMRESULT ret = waveInReset(m_hWaveIn); if (ret != MMSYSERR_NOERROR) { throw TException("Unable to add a waveheader"); } } //-------------------------------------------------------------------- void WinSoundInputDevice::stop() { MMRESULT ret = waveInStop(m_hWaveIn); if (ret != MMSYSERR_NOERROR) { throw TException("Unable to add a waveheader"); } } //==================================================================== #ifndef TNZCORE_LIGHT class RecordTask : public TThread::Runnable { public: RecordTask(TSoundInputDeviceImp *dev) : Runnable(), m_dev(dev) {} ~RecordTask() {} void run(); TSoundInputDeviceImp *m_dev; }; #endif //==================================================================== class TSoundInputDeviceImp : public WinSoundInputDevice { public: bool m_allocateBuff; bool m_isRecording; bool m_supportVolume; int m_index; TINT32 m_byteRecorded; TSoundTrackP m_st; TSoundTrackFormat m_format; #ifndef TNZCORE_LIGHT TThread::Executor m_executor; #endif vector m_whdr; vector m_recordedBlocks; std::set m_supportedRate; HANDLE m_hLastBlockDone; TSoundInputDeviceImp(); ~TSoundInputDeviceImp(); void insertAllRate(); bool verifyRate(); }; //------------------------------------------------------------------------------ TSoundInputDeviceImp::TSoundInputDeviceImp() : m_allocateBuff(false), m_isRecording(false), m_supportVolume(false), m_index(0), m_byteRecorded(0), m_format(), m_whdr(3), m_recordedBlocks(), m_supportedRate() { m_hLastBlockDone = CreateEvent( NULL, // no security attributes FALSE, // is manual-reset event? FALSE, // is signaled? NULL); // object not named } //------------------------------------------------------------------------------ TSoundInputDeviceImp::~TSoundInputDeviceImp() { if (m_isRecording) { try { reset(); WaitForSingleObject(m_hLastBlockDone, INFINITE); int i; for (i = 0; i < (int)m_recordedBlocks.size(); ++i) delete[] m_recordedBlocks[i]; close(); } catch (TException &) { } } CloseHandle(m_hLastBlockDone); } //---------------------------------------------------------------------------- void TSoundInputDeviceImp::insertAllRate() { m_supportedRate.insert(8000); m_supportedRate.insert(11025); m_supportedRate.insert(16000); m_supportedRate.insert(22050); m_supportedRate.insert(32000); m_supportedRate.insert(44100); m_supportedRate.insert(48000); } //---------------------------------------------------------------------------- bool TSoundInputDeviceImp::verifyRate() { std::set::iterator it; for (it = m_supportedRate.begin(); it != m_supportedRate.end();) { MMRESULT ret; WAVEFORMATEX wf; wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = 1; wf.nSamplesPerSec = *it; wf.wBitsPerSample = 8; wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) >> 3; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; wf.cbSize = 0; ret = waveInOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); if (ret == MMSYSERR_NOERROR) { ++it; continue; } if (ret == WAVERR_BADFORMAT) it = m_supportedRate.erase(it); else return false; } if (m_supportedRate.end() == m_supportedRate.begin()) return false; return true; } //==================================================================== namespace { void CALLBACK recordCB( HWAVEIN hwi, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) { WAVEHDR *whdr = (WAVEHDR *)dwParam1; HANDLE *blockDone = (HANDLE *)dwInstance; if (uMsg != MM_WIM_DATA) return; SetEvent(blockDone); } } //============================================================================== TSoundInputDevice::TSoundInputDevice() : m_imp(new TSoundInputDeviceImp()) { m_imp->insertAllRate(); if (!m_imp->verifyRate()) throw TSoundDeviceException( TSoundDeviceException::FailedInit, "Unable to verify supported rates"); if (supportsVolume()) m_imp->m_supportVolume = true; } //------------------------------------------------------------------------------ TSoundInputDevice::~TSoundInputDevice() { delete m_imp; } //------------------------------------------------------------------------------ bool TSoundInputDevice::installed() { int ndev = waveInGetNumDevs(); if (ndev <= 0) return false; return true; } //------------------------------------------------------------------------------ #ifndef TNZCORE_LIGHT void TSoundInputDevice::record(const TSoundTrackFormat &format, Source devtype) { if (m_imp->m_isRecording) throw TSoundDeviceException( TSoundDeviceException::Busy, "Just another recoding is in progress"); /*if ((format.m_bitPerSample == 8 && format.m_signedSample) || (format.m_bitPerSample == 24)) throw TException("This format is not supported for recording");*/ try { TSoundTrackFormat fmt = getPreferredFormat(format); if (fmt != format) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, "Unsupported Format"); } } catch (TSoundDeviceException &e) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, e.getMessage()); } if (!setRecordLine(devtype)) throw TSoundDeviceException( TSoundDeviceException::UnableSetDevice, "Problems to set input source line to record"); m_imp->m_format = format; m_imp->m_st = 0; ResetEvent(m_imp->m_hLastBlockDone); ResetEvent(m_imp->m_hBlockDone); m_imp->m_allocateBuff = true; m_imp->m_isRecording = true; m_imp->m_index = 0; m_imp->m_recordedBlocks.clear(); m_imp->m_byteRecorded = 0; TINT32 bytePerSec = format.m_sampleRate * ((format.m_bitPerSample * format.m_channelCount) >> 3); try { WaveFormat wf(m_imp->m_format.m_channelCount, m_imp->m_format.m_sampleRate, m_imp->m_format.m_bitPerSample); m_imp->open(wf); } catch (TException &e) { m_imp->m_isRecording = false; throw TSoundDeviceException( TSoundDeviceException::UnableOpenDevice, e.getMessage()); } for (; m_imp->m_index < (int)(m_imp->m_whdr.size() - 1); ++m_imp->m_index) { try { m_imp->prepareHeader(new char[bytePerSec], bytePerSec, m_imp->m_whdr[m_imp->m_index]); m_imp->addBlock(m_imp->m_whdr[m_imp->m_index]); } catch (TException &e) { m_imp->m_isRecording = false; for (int j = 0; j < (int)(m_imp->m_whdr.size() - 1); ++j) { if (m_imp->m_whdr[j].dwFlags & WHDR_PREPARED) { try { m_imp->unprepareHeader(m_imp->m_whdr[j]); } catch (TException &e) { throw TSoundDeviceException( TSoundDeviceException::UnableCloseDevice, e.getMessage()); } delete[] m_imp->m_whdr[j].lpData; } else if (j == m_imp->m_index) delete[] m_imp->m_whdr[j].lpData; } throw TSoundDeviceException( TSoundDeviceException::UnablePrepare, e.getMessage()); } } m_imp->m_executor.addTask(new RecordTask(m_imp)); } //------------------------------------------------------------------------------ void TSoundInputDevice::record(const TSoundTrackP &st, Source devtype) { if (m_imp->m_isRecording) throw TSoundDeviceException( TSoundDeviceException::Busy, "Just another recoding is in progress"); m_imp->m_format = st->getFormat(); /*if ((m_imp->m_format.m_bitPerSample == 8 && m_imp->m_format.m_signedSample) || (m_imp->m_format.m_bitPerSample == 24)) throw TException("This format is not supported for recording");*/ try { TSoundTrackFormat fmt = getPreferredFormat(st->getFormat()); if (fmt != st->getFormat()) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, "Unsupported Format"); } } catch (TSoundDeviceException &e) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, e.getMessage()); } if (!setRecordLine(devtype)) throw TSoundDeviceException( TSoundDeviceException::UnableSetDevice, "Problems to set input source line to record"); m_imp->m_st = st; m_imp->m_allocateBuff = false; m_imp->m_isRecording = true; ResetEvent(m_imp->m_hLastBlockDone); ResetEvent(m_imp->m_hBlockDone); m_imp->m_index = 0; m_imp->m_recordedBlocks.clear(); m_imp->m_byteRecorded = 0; try { WaveFormat wf(m_imp->m_format.m_channelCount, m_imp->m_format.m_sampleRate, m_imp->m_format.m_bitPerSample); m_imp->open(wf); m_imp->prepareHeader((char *)st->getRawData(), st->getSampleCount() * ((st->getBitPerSample() * st->getChannelCount()) >> 3), m_imp->m_whdr[m_imp->m_index]); m_imp->addBlock(m_imp->m_whdr[m_imp->m_index]); } catch (TException &e) { m_imp->m_isRecording = false; if (m_imp->m_whdr[m_imp->m_index].dwFlags & WHDR_PREPARED) m_imp->unprepareHeader(m_imp->m_whdr[m_imp->m_index]); throw TSoundDeviceException( TSoundDeviceException::UnablePrepare, e.getMessage()); } m_imp->m_executor.addTask(new RecordTask(m_imp)); } #endif //------------------------------------------------------------------------------ TSoundTrackP TSoundInputDevice::stop() { if (!m_imp->m_isRecording) return 0; m_imp->m_isRecording = false; try { m_imp->reset(); } catch (TException &e) { for (int j = 0; j < (int)m_imp->m_whdr.size(); ++j) { if (m_imp->m_whdr[j].dwFlags & WHDR_PREPARED) { try { m_imp->unprepareHeader(m_imp->m_whdr[j]); } catch (TException &e) { throw TSoundDeviceException( TSoundDeviceException::UnablePrepare, e.getMessage()); } delete[] m_imp->m_whdr[j].lpData; } } throw TSoundDeviceException( TSoundDeviceException::UnableCloseDevice, e.getMessage()); } if (m_imp->m_allocateBuff) { WaitForSingleObject(m_imp->m_hLastBlockDone, INFINITE); TSoundTrackP st = TSoundTrack::create( m_imp->m_format, m_imp->m_byteRecorded / ((m_imp->m_format.m_bitPerSample * m_imp->m_format.m_channelCount) >> 3)); TINT32 bytePerSec = m_imp->m_format.m_sampleRate * ((m_imp->m_format.m_bitPerSample * m_imp->m_format.m_channelCount) >> 3); int i; for (i = 0; i < (int)(m_imp->m_recordedBlocks.size() - 1); ++i) { memcpy( (void *)(st->getRawData() + bytePerSec * i), m_imp->m_recordedBlocks[i], bytePerSec); delete[] m_imp->m_recordedBlocks[i]; } TINT32 lastBlockSize = m_imp->m_byteRecorded - (bytePerSec * i); if (lastBlockSize != 0) { memcpy((void *)(st->getRawData() + bytePerSec * i), m_imp->m_recordedBlocks[i], lastBlockSize); delete[] m_imp->m_recordedBlocks[i]; } try { m_imp->close(); } catch (TException &e) { throw TSoundDeviceException( TSoundDeviceException::UnableCloseDevice, e.getMessage()); } return st; } else { WaitForSingleObject(m_imp->m_hLastBlockDone, INFINITE); try { m_imp->close(); } catch (TException &e) { throw TSoundDeviceException( TSoundDeviceException::UnableCloseDevice, e.getMessage()); } return m_imp->m_st; } } //------------------------------------------------------------------------------ #ifndef TNZCORE_LIGHT void RecordTask::run() { m_dev->start(); if (m_dev->m_allocateBuff) { TINT32 bytePerSec = m_dev->m_format.m_sampleRate * ((m_dev->m_format.m_bitPerSample * m_dev->m_format.m_channelCount) >> 3); while (m_dev->m_whdr[(m_dev->m_index + 1) % m_dev->m_whdr.size()].dwFlags & WHDR_PREPARED) { if (m_dev->m_isRecording) WaitForSingleObject(m_dev->m_hBlockDone, INFINITE); int indexToPrepare = m_dev->m_index; // calcolo l'indice successivo per far l'unprepare m_dev->m_index = (m_dev->m_index + 1) % m_dev->m_whdr.size(); if (m_dev->m_whdr[m_dev->m_index].dwFlags & WHDR_DONE) { TINT32 byteRecorded = m_dev->m_whdr[m_dev->m_index].dwBytesRecorded; if (byteRecorded) { m_dev->m_recordedBlocks.push_back(m_dev->m_whdr[m_dev->m_index].lpData); m_dev->m_byteRecorded += byteRecorded; } try { m_dev->unprepareHeader(m_dev->m_whdr[m_dev->m_index]); } catch (TException &) { for (int i = 0; i < (int)m_dev->m_recordedBlocks.size(); ++i) delete[] m_dev->m_recordedBlocks[i]; return; } if (byteRecorded == 0) { delete[] m_dev->m_whdr[m_dev->m_index].lpData; } // con questo controllo si evita che vengano accodati nuovi blocchi // dopo che e' stata chiamata la waveInReset if (m_dev->m_isRecording) { try { m_dev->prepareHeader(new char[bytePerSec], bytePerSec, m_dev->m_whdr[indexToPrepare]); m_dev->addBlock(m_dev->m_whdr[indexToPrepare]); } catch (TException &) { m_dev->m_isRecording = false; for (int i = 0; i < (int)m_dev->m_recordedBlocks.size(); ++i) delete[] m_dev->m_recordedBlocks[i]; return; } } } else m_dev->m_index = indexToPrepare; } } else { if (m_dev->m_isRecording) WaitForSingleObject(m_dev->m_hBlockDone, INFINITE); try { m_dev->unprepareHeader(m_dev->m_whdr[m_dev->m_index]); m_dev->m_isRecording = false; } catch (TException &) { m_dev->m_isRecording = false; return; } } SetEvent(m_dev->m_hLastBlockDone); return; } //------------------------------------------------------------------------------ #endif double TSoundInputDevice::getVolume() { DWORD dwVolumeControlID; UINT nNumMixers; MMRESULT ret; MIXERLINE mxl; nNumMixers = mixerGetNumDevs(); if (nNumMixers == 0) throw TSoundDeviceException( TSoundDeviceException::NoMixer, "Doesn't exist a audio mixer device"); // get dwLineID ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); if (ret != MMSYSERR_NOERROR) throw TSoundDeviceException( TSoundDeviceException::UnableVolume, "Error to obtain info by mixer"); // get dwControlID MIXERCONTROL mxc; ret = getLineControl(mxc, (HMIXEROBJ)0, mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME); if (ret == MIXERR_INVALCONTROL) throw TSoundDeviceException( TSoundDeviceException::UnableVolume, "Is not possible to obtain info of volume by mixer"); if (ret != MMSYSERR_NOERROR) throw TSoundDeviceException( TSoundDeviceException::UnableVolume, "Error to obtain info by mixer"); dwVolumeControlID = mxc.dwControlID; MIXERCONTROLDETAILS_UNSIGNED mxcdVolume; ret = getControlDetails((HMIXEROBJ)0, dwVolumeControlID, mxc.cMultipleItems, &mxcdVolume); if (ret != MMSYSERR_NOERROR) throw TSoundDeviceException( TSoundDeviceException::UnableVolume, "Error to obtain info by mixer"); DWORD dwVal = mxcdVolume.dwValue; return (double)dwVal * 10.0 / (double)mxc.Bounds.dwMaximum; } //------------------------------------------------------------------------------ bool TSoundInputDevice::setVolume(double value) { DWORD dwVolumeControlID, dwMaximum; UINT nNumMixers; MMRESULT ret; MIXERLINE mxl; nNumMixers = mixerGetNumDevs(); if (nNumMixers == 0) throw TSoundDeviceException( TSoundDeviceException::NoMixer, "Doesn't exist a audio mixer device"); // get dwLineID ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); if (ret != MMSYSERR_NOERROR) throw TException("Error to obtain info by mixer"); // get dwControlID MIXERCONTROL mxc; ret = getLineControl(mxc, (HMIXEROBJ)0, mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME); if (ret != MMSYSERR_NOERROR) throw TSoundDeviceException( TSoundDeviceException::UnableVolume, "Error to obtain info by mixer"); dwMaximum = mxc.Bounds.dwMaximum; dwVolumeControlID = mxc.dwControlID; int newValue; double fattProp = ((double)(mxc.Metrics.cSteps - 1) * value) / 10; double delta = (double)(dwMaximum / (mxc.Metrics.cSteps - 1)); newValue = (int)(tround(fattProp) * delta); MIXERCONTROLDETAILS_UNSIGNED mxcdVolume = {newValue}; ret = setControlDetails((HMIXEROBJ)0, dwVolumeControlID, mxc.cMultipleItems, &mxcdVolume); if (ret != MMSYSERR_NOERROR) throw TSoundDeviceException( TSoundDeviceException::UnableVolume, "Error to obtain info by mixer"); return true; } //------------------------------------------------------------------------------ bool TSoundInputDevice::supportsVolume() { UINT nNumMixers; MMRESULT ret; MIXERLINE mxl; nNumMixers = mixerGetNumDevs(); if (nNumMixers == 0) throw TSoundDeviceException( TSoundDeviceException::NoMixer, "Doesn't exist a audio mixer device"); // get dwLineID ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); if (ret != MMSYSERR_NOERROR) throw TSoundDeviceException( TSoundDeviceException::UnableVolume, "Error to obtain info by mixer"); // get dwControlID MIXERCONTROL mxc; ret = getLineControl(mxc, (HMIXEROBJ)0, mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_VOLUME); if (ret == MIXERR_INVALCONTROL) return false; if (ret != MMSYSERR_NOERROR) throw TSoundDeviceException( TSoundDeviceException::UnableVolume, "Error to obtain info by mixer"); return true; } //------------------------------------------------------------------------------ bool TSoundInputDevice::isRecording() { return m_imp->m_isRecording; } //------------------------------------------------------------------------------ /*TSoundTrackFormat TSoundInputDevice::getPreferredFormat( ULONG sampleRate, int channelCount, int bitPerSample) { MMRESULT ret; TSoundTrackFormat fmt; ret = isaFormatSupported(sampleRate, channelCount, bitPerSample, true); if (ret == MMSYSERR_NOERROR) { fmt.m_bitPerSample = bitPerSample; fmt.m_channelCount = channelCount; fmt.m_sampleRate = sampleRate; if (bitPerSample >= 16) fmt.m_signedSample = true; else fmt.m_signedSample = false; return fmt; } if (ret == WAVERR_BADFORMAT) { //avvvicinarsi al sample rate => dovrebbe esser OK avendo selezionato i piu' vicini std::set::iterator it = m_imp->m_supportedRate.lower_bound(sampleRate); if (it == m_imp->m_supportedRate.end()) { it = std::max_element(m_imp->m_supportedRate.begin(), m_imp->m_supportedRate.end()); if (it != m_imp->m_supportedRate.end()) sampleRate = *(m_imp->m_supportedRate.rbegin()); else throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, "There isn't a supported format"); } else sampleRate = *it; ret = isaFormatSupported(sampleRate, channelCount, bitPerSample, true); if (ret == MMSYSERR_NOERROR) { fmt.m_bitPerSample = bitPerSample; fmt.m_channelCount = channelCount; fmt.m_sampleRate = sampleRate; if (bitPerSample >= 16) fmt.m_signedSample = true; else fmt.m_signedSample = false; return fmt; } if (ret == WAVERR_BADFORMAT) { //cambiare bps if (bitPerSample <= 8) bitPerSample = 8; else if ((bitPerSample > 8 && bitPerSample < 16) || bitPerSample >= 16) bitPerSample = 16; ret = isaFormatSupported(sampleRate, channelCount, bitPerSample, true); if (ret == MMSYSERR_NOERROR) { fmt.m_bitPerSample = bitPerSample; fmt.m_channelCount = channelCount; fmt.m_sampleRate = sampleRate; if (bitPerSample >= 16) fmt.m_signedSample = true; else fmt.m_signedSample = false; return fmt; } if (ret == WAVERR_BADFORMAT) { //switch mono/stereo if (channelCount <= 1) channelCount = 1; else channelCount = 2; ret = isaFormatSupported(sampleRate, channelCount, bitPerSample, true); if (ret == MMSYSERR_NOERROR) { fmt.m_bitPerSample = bitPerSample; fmt.m_channelCount = channelCount; fmt.m_sampleRate = sampleRate; if (bitPerSample >= 16) fmt.m_signedSample = true; else fmt.m_signedSample = false; return fmt; } if (ret == WAVERR_BADFORMAT) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, "Doesn't exist a preferred format"); } } } } throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, "Error to query supported format"); } */ TSoundTrackFormat TSoundInputDevice::getPreferredFormat( TUINT32 sampleRate, int channelCount, int bitPerSample) { TSoundTrackFormat fmt; //avvvicinarsi al sample rate => dovrebbe esser OK avendo selezionato i piu' vicini std::set::iterator it = m_imp->m_supportedRate.lower_bound(sampleRate); if (it == m_imp->m_supportedRate.end()) { it = std::max_element(m_imp->m_supportedRate.begin(), m_imp->m_supportedRate.end()); if (it != m_imp->m_supportedRate.end()) sampleRate = *(m_imp->m_supportedRate.rbegin()); else throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, "There isn't a supported format"); } else sampleRate = *it; if (bitPerSample <= 8) bitPerSample = 8; else if ((bitPerSample > 8 && bitPerSample < 16) || bitPerSample >= 16) bitPerSample = 16; if (bitPerSample >= 16) fmt.m_signedSample = true; else fmt.m_signedSample = false; //switch mono/stereo if (channelCount <= 1) channelCount = 1; else channelCount = 2; fmt.m_bitPerSample = bitPerSample; fmt.m_channelCount = channelCount; fmt.m_sampleRate = sampleRate; return fmt; } //------------------------------------------------------------------------------ TSoundTrackFormat TSoundInputDevice::getPreferredFormat(const TSoundTrackFormat &format) { try { return getPreferredFormat( format.m_sampleRate, format.m_channelCount, format.m_bitPerSample); } catch (TSoundDeviceException &e) { throw TSoundDeviceException( TSoundDeviceException::UnsupportedFormat, e.getMessage()); } } //============================================================================== //============================================================================== // Funzioni per l'interazione con il mixer device //============================================================================== //============================================================================== namespace { // restituisce dentro la struttura mxc le informazioni relative // al controllo di tipo dwControlType associato alla linea // identificata da dwLineID MMRESULT getLineControl(MIXERCONTROL &mxc, HMIXEROBJ hMixer, DWORD dwLineID, DWORD dwControlType) { MIXERLINECONTROLS mxlc; mxlc.cbStruct = sizeof(MIXERLINECONTROLS); mxlc.dwLineID = dwLineID; mxlc.dwControlType = dwControlType; mxlc.cControls = 1; mxlc.cbmxctrl = sizeof(MIXERCONTROL); mxlc.pamxctrl = &mxc; MMRESULT ret = mixerGetLineControls((HMIXEROBJ)hMixer, &mxlc, MIXER_OBJECTF_HMIXER | MIXER_GETLINECONTROLSF_ONEBYTYPE); return ret; } //------------------------------------------------------------------------------ // restituisce nella struttura mxl le informazioni relative alla linea // sorgente individuata dagli estremi destination e source MMRESULT getLineInfo(HMIXEROBJ hMixer, MIXERLINE &mxl, DWORD destination, DWORD source) { MMRESULT ret; mxl.cbStruct = sizeof(mxl); mxl.dwDestination = destination; mxl.dwSource = source; ret = mixerGetLineInfo(0, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_SOURCE); return ret; } //------------------------------------------------------------------------------ // restituisce nella struttura mxl le informazioni relative alla linea // individuata da dwLineID MMRESULT getLineInfo(HMIXEROBJ hMixer, MIXERLINE &mxl, DWORD dwLineID) { MMRESULT ret; mxl.cbStruct = sizeof(mxl); mxl.dwLineID = dwLineID; ret = mixerGetLineInfo((HMIXEROBJ)hMixer, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_LINEID); return ret; } //------------------------------------------------------------------------------ // restituisce nella struttura mxl le informazioni relative alla linea // individuata dal tipo specificato in dwComponentType MMRESULT getLineInfo(HMIXEROBJ hMixer, DWORD dwComponentType, MIXERLINE &mxl) { MMRESULT ret; mxl.cbStruct = sizeof(MIXERLINE); mxl.dwComponentType = dwComponentType; ret = mixerGetLineInfo((HMIXEROBJ)hMixer, &mxl, MIXER_OBJECTF_HMIXER | MIXER_GETLINEINFOF_COMPONENTTYPE); return ret; } //------------------------------------------------------------------------------ // consente di settare il valore booleano specificato in mxcdSelectValue // relativo al controllo specificato in dwSelectControlID MMRESULT setControlDetails(HMIXEROBJ hMixer, DWORD dwSelectControlID, DWORD dwMultipleItems, MIXERCONTROLDETAILS_BOOLEAN *mxcdSelectValue) { MMRESULT ret; MIXERCONTROLDETAILS mxcd; mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = dwSelectControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = dwMultipleItems; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_BOOLEAN); mxcd.paDetails = mxcdSelectValue; ret = mixerSetControlDetails((HMIXEROBJ)hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); return ret; } //------------------------------------------------------------------------------ // consente di settare il valore UNSIGNED specificato in mxcdSelectValue // relativo al controllo specificato in dwSelectControlID MMRESULT setControlDetails(HMIXEROBJ hMixer, DWORD dwSelectControlID, DWORD dwMultipleItems, MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue) { MMRESULT ret; MIXERCONTROLDETAILS mxcd; mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = dwSelectControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = dwMultipleItems; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); mxcd.paDetails = mxcdSelectValue; ret = mixerSetControlDetails((HMIXEROBJ)hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); return ret; } //------------------------------------------------------------------------------ // consente di ottenere il valore UNSIGNED specificato in mxcdSelectValue // relativo al controllo specificato in dwSelectControlID MMRESULT getControlDetails(HMIXEROBJ hMixer, DWORD dwSelectControlID, DWORD dwMultipleItems, MIXERCONTROLDETAILS_UNSIGNED *mxcdSelectValue) { MMRESULT ret; MIXERCONTROLDETAILS mxcd; mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = dwSelectControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = dwMultipleItems; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED); mxcd.paDetails = mxcdSelectValue; ret = mixerGetControlDetails((HMIXEROBJ)hMixer, &mxcd, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); return ret; } //------------------------------------------------------------------------------ // consente di ottenere la lista di informazioni in pmxcdSelectText // relativo al controllo specificato in dwSelectControlID MMRESULT getControlDetails(HMIXEROBJ hMixer, DWORD dwSelectControlID, DWORD dwMultipleItems, MIXERCONTROLDETAILS_LISTTEXT *pmxcdSelectText) { MMRESULT ret; MIXERCONTROLDETAILS mxcd; mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS); mxcd.dwControlID = dwSelectControlID; mxcd.cChannels = 1; mxcd.cMultipleItems = dwMultipleItems; mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXT); mxcd.paDetails = pmxcdSelectText; ret = mixerGetControlDetails((HMIXEROBJ)0, &mxcd, MIXER_GETCONTROLDETAILSF_LISTTEXT); return ret; } //------------------------------------------------------------------------------ // restituiscei l nome della linea identificata da lineID string getMixerLineName(DWORD lineID) { MIXERLINE mxl; MMRESULT ret; ret = getLineInfo((HMIXEROBJ)0, mxl, lineID); #ifdef TNZCORE_LIGHT assert(false); return ""; #else return string(mxl.szName); #endif } //------------------------------------------------------------------------------ // restituisce la lista degli identificativi delle linee sorgente associate // alla destinazione di tipo dstComponentType list getMixerSrcLines(DWORD dstComponentType) { list srcList; MMRESULT ret; MIXERLINE mxl; ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); if (ret != MMSYSERR_NOERROR) //forse bisognerebbe lanciare un'eccezione return srcList; //non ha linea di dst per la registrazione int v; for (v = 0; v < (int)mxl.cConnections; v++) { MIXERLINE mxl1; ret = getLineInfo((HMIXEROBJ)0, mxl1, mxl.dwDestination, v); if (ret == MMSYSERR_NOERROR) srcList.push_back(mxl1.dwLineID); } return srcList; } //------------------------------------------------------------------------------ // restituisce la lista degli identificativi delle linee sorgente di tipo // srcComponentType associate alla destinazione di tipo dstComponentType list getMixerSrcLines(DWORD dstComponentType, DWORD srcComponentType) { list srcList; MMRESULT ret; MIXERLINE mxl; ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); if (ret != MMSYSERR_NOERROR) //forse bisognerebbe lanciare un'eccezione return srcList; //non ha linea di dst per la registrazione int v; for (v = 0; v < (int)mxl.cConnections; v++) { MIXERLINE mxl1; ret = getLineInfo((HMIXEROBJ)0, mxl1, mxl.dwDestination, v); if (ret == MMSYSERR_NOERROR) if (mxl1.dwComponentType == srcComponentType) srcList.push_back(mxl1.dwLineID); } return srcList; } //------------------------------------------------------------------------------ // restituisce true sse la linea destinazione di tipo dstComponentType // supporta una linea sorgente di tipo srcComponentType bool isSrcLineSupported(DWORD dstComponentType, DWORD srcComponentType) { // ci possono essere piu' linee sorgente dello stesso tipo in // corrispondenza di una data linea destinazione ? MMRESULT ret; MIXERLINE mxl; ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); if (ret != MMSYSERR_NOERROR) return false; //non ha linea di dst per la registrazione int v; for (v = 0; v < (int)mxl.cConnections; v++) { MIXERLINE mxl1; ret = getLineInfo((HMIXEROBJ)0, mxl1, mxl.dwDestination, v); if (ret == MMSYSERR_NOERROR) if (mxl1.dwComponentType == srcComponentType) return true; } return false; } //------------------------------------------------------------------------------ bool activateSrcLine(const MIXERLINE &mxlDst, DWORD componentTypeSrc) { if (!isSrcLineSupported(mxlDst.dwComponentType, componentTypeSrc)) return false; bool bRetVal = true; for (DWORD v = 0; v < mxlDst.cConnections; v++) { MIXERLINE mxlSrc; MMRESULT ret = getLineInfo((HMIXEROBJ)0, mxlSrc, mxlDst.dwDestination, v); if (ret == MMSYSERR_NOERROR) { // chiedo il controllo di tipo MUTE della linea sorgente MIXERCONTROL mxc; ret = getLineControl( mxc, (HMIXEROBJ)0, mxlSrc.dwLineID, MIXERCONTROL_CONTROLTYPE_MUTE); if (ret == MMSYSERR_NOERROR) { MIXERCONTROLDETAILS_BOOLEAN mxcdSelectValue; mxcdSelectValue.fValue = mxlSrc.dwComponentType == componentTypeSrc ? 0L : 1L; ret = setControlDetails((HMIXEROBJ)0, mxc.dwControlID, mxc.cMultipleItems, &mxcdSelectValue); if (ret != MMSYSERR_NOERROR) bRetVal = false; } } } return bRetVal; } //------------------------------------------------------------------------------ bool setSrcMixMuxControl(MIXERCONTROL mxc, DWORD componentTypeSrc) { MMRESULT ret; DWORD dwIndexLine; bool found = false; // mantengo nota del ID del controllo dsst individuato e // del numero di linee src ad esso associate DWORD dwSelectControlID = mxc.dwControlID; DWORD dwMultipleItems = mxc.cMultipleItems; if (dwMultipleItems == 0) return false; // determino l'indice dell'item corrispondente alla linea sorgente // di tipo componentTypeSrc MIXERCONTROLDETAILS_LISTTEXT *pmxcdSelectText = new MIXERCONTROLDETAILS_LISTTEXT[dwMultipleItems]; if (pmxcdSelectText != NULL) { // estraggo le info su tutte le linee associate al controllo ret = getControlDetails((HMIXEROBJ)0, dwSelectControlID, dwMultipleItems, pmxcdSelectText); if (ret == MMSYSERR_NOERROR) { for (DWORD dwi = 0; dwi < dwMultipleItems; dwi++) { // prendo le info su ogni linea e verifico se e' del giusto tipo MIXERLINE mxl; ret = getLineInfo((HMIXEROBJ)0, mxl, pmxcdSelectText[dwi].dwParam1); if (ret == MMSYSERR_NOERROR && mxl.dwComponentType == componentTypeSrc) { dwIndexLine = dwi; found = true; break; } } } delete[] pmxcdSelectText; if (!found) return false; } if (dwIndexLine >= dwMultipleItems) return false; bool bRetVal = false; MIXERCONTROLDETAILS_BOOLEAN *pmxcdSelectValue = new MIXERCONTROLDETAILS_BOOLEAN[dwMultipleItems]; if (pmxcdSelectValue != NULL) { ::ZeroMemory(pmxcdSelectValue, dwMultipleItems * sizeof(MIXERCONTROLDETAILS_BOOLEAN)); // impostazione del valore pmxcdSelectValue[dwIndexLine].fValue = (TINT32)1; // lVal; //dovrebbe esser uno ret = setControlDetails((HMIXEROBJ)0, dwSelectControlID, dwMultipleItems, pmxcdSelectValue); if (ret == MMSYSERR_NOERROR) bRetVal = true; delete[] pmxcdSelectValue; } return bRetVal; } //------------------------------------------------------------------------------ bool setRecordLine(TSoundInputDevice::Source typeInput) { DWORD dwComponentTypeSrc; UINT nNumMixers; MMRESULT ret; MIXERLINE mxl = {0}; switch (typeInput) { case TSoundInputDevice::LineIn: dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_LINE /*| MIXERLINE_COMPONENTTYPE_SRC_AUXILIARY | MIXERLINE_COMPONENTTYPE_SRC_ANALOG*/; break; case TSoundInputDevice::DigitalIn: dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_DIGITAL; break; case TSoundInputDevice::CdAudio: dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_COMPACTDISC; break; default: dwComponentTypeSrc = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; } nNumMixers = mixerGetNumDevs(); if (nNumMixers == 0) return false; // utilizziamo il MIXER di default identificato dall'indice 0 // vedo se il device ha una linea dst per il wave_input ret = getLineInfo((HMIXEROBJ)0, MIXERLINE_COMPONENTTYPE_DST_WAVEIN, mxl); if (ret != MMSYSERR_NOERROR) return false; //non ha linea di dst per la registrazione //vediamo che tipo controllo ha questa linea dst // sara' un MIXER? MIXERCONTROL mxc = {0}; ret = getLineControl(mxc, (HMIXEROBJ)0, mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_MIXER); if (ret != MMSYSERR_NOERROR) { // no mixer, try MUX ret = getLineControl(mxc, (HMIXEROBJ)0, mxl.dwLineID, MIXERCONTROL_CONTROLTYPE_MUX); if (ret != MMSYSERR_NOERROR) { // vediamo se e' uno di quei device ne' MIXER ne' MUX return activateSrcLine(mxl, dwComponentTypeSrc); } else { // la linea ha un controllo di tipo MUX return setSrcMixMuxControl(mxc, dwComponentTypeSrc); } } else { // la linea ha un controllo di tipo MIXER return setSrcMixMuxControl(mxc, dwComponentTypeSrc); } } //------------------------------------------------------------------------------ MMRESULT isaFormatSupported( int sampleRate, int channelCount, int bitPerSample, bool input) { WAVEFORMATEX wf; MMRESULT ret; wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = channelCount; wf.nSamplesPerSec = sampleRate; wf.wBitsPerSample = bitPerSample; wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) >> 3; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; wf.cbSize = 0; if (input) ret = waveInOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); else ret = waveOutOpen(NULL, WAVE_MAPPER, &wf, NULL, NULL, WAVE_FORMAT_QUERY); return ret; } }