Форум программистов, компьютерный форум, киберфорум
Наши страницы

Пишу игровой движок на C++. 034. Звук. Чтение wav файла

Войти
Регистрация
Восстановить пароль
Сайт движка - gost.imsoftworks.info
Исходные коды движка - https://github.com/532235/GoST
Документация
Примеры кода программ - https://github.com/532235/GoST/wiki

Другой хобби-проект в группе в вк https://vk.com/club154291467
Оценить эту запись

Пишу игровой движок на C++. 034. Звук. Чтение wav файла

Запись от 532235 размещена 30.01.2018 в 21:24
Метки c++, wav, движок, звук

Сделал базу для звука, gtAudioSystem и т.д. Загрузку wav и ogg файлов. Именно загрузку.

Пока напишу просто о загрузке wav файла.


Для каждого формата файлов сделал отдельные классы.

Изначально делал по примеру Microsoft из DirectX SDK.

Ужасающий код. Его было так много, что в классе `Wave` был только один метод на чтение данных.
Всё разделять на методы было бы очень трудоёмкой задачей.

Вот что было. Используются средства от Microsoft.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
gtAudioSourceImpl* Wave::read( const gtString& fileName ){
 
    gtAudioSourceImpl* source = new gtAudioSourceImpl;
 
    gtAudioSourceInfo info;
 
    if( !source ) return nullptr;
 
    HRESULT hr;
    m_dwFlags = 1;
 
    m_hmmio = mmioOpen( (wchar_t*)fileName.c_str(), NULL, MMIO_ALLOCBUF | MMIO_READ );
    if( NULL == m_hmmio ){
        gtLogWriter::printWarning( u"Can not open file for read" );
        delete source;
        return nullptr;
    }
 
        /// chunk info. for general use.
    MMCKINFO ckIn;
    memset( &ckIn, 0, sizeof(ckIn) );
 
        /// Temp PCM structure to load in.
    PCMWAVEFORMAT pcmWaveFormat;
 
    if( ( 0 != mmioDescend( m_hmmio, &m_ckRiff, NULL, 0 ) ) ){
        gtLogWriter::printWarning( u"mmioDescend == 0" );
        delete source;
        return nullptr;
    }
 
        /// Check to make sure this is a valid wave file
    if( ( m_ckRiff.ckid != FOURCC_RIFF ) ||
        ( m_ckRiff.fccType != mmioFOURCC( 'W', 'A', 'V', 'E' ) ) ){
        gtLogWriter::printWarning( u"Bad magic" );
        delete source;
        return nullptr;
    }
 
        /// Search the input file for for the 'fmt ' chunk.
    ckIn.ckid = mmioFOURCC( 'f', 'm', 't', ' ' );
    if( 0 != mmioDescend( m_hmmio, &ckIn, &m_ckRiff, MMIO_FINDCHUNK ) ){
        gtLogWriter::printWarning( u"Can not Search the input file for for the 'fmt ' chunk" );
        delete source;
        return nullptr;
    }
 
        /// Expect the 'fmt' chunk to be at least as large as <PCMWAVEFORMAT>;
        /// if there are extra parameters at the end, we'll ignore them
    if( ckIn.cksize < ( LONG )sizeof( PCMWAVEFORMAT ) ){
        gtLogWriter::printWarning( u"'fmt' chunk large as <PCMWAVEFORMAT>" );
        delete source;
        return nullptr;
    }
 
        /// Read the 'fmt ' chunk into <pcmWaveFormat>.
    if( mmioRead( m_hmmio, ( HPSTR )&pcmWaveFormat,
                  sizeof( pcmWaveFormat ) ) != sizeof( pcmWaveFormat ) ){
        gtLogWriter::printWarning( u"Can not read the 'fmt ' chunk into <pcmWaveFormat>" );
        delete source;
        return nullptr;
    }
 
        /// Allocate the waveformatex, but if its not pcm format, read the next
        /// word, and thats how many extra bytes to allocate.
    if( pcmWaveFormat.wf.wFormatTag == WAVE_FORMAT_PCM ){
        /*m_pwfx = ( WAVEFORMATEX* )new CHAR[ sizeof( WAVEFORMATEX ) ];
        if( NULL == m_pwfx ){
            gtLogWriter::printWarning( u"Can not allocate memmory for WAVEFORMATEX" );
            return nullptr;
        }*/
 
            /// Copy the bytes from the pcm structure to the waveformatex structure
        //memcpy( m_pwfx, &pcmWaveFormat, sizeof( pcmWaveFormat ) );
        //m_pwfx->cbSize = 0;
 
        info.m_AvgBytesPerSec   = pcmWaveFormat.wf.nAvgBytesPerSec;
        info.m_BitsPerSample    = pcmWaveFormat.wBitsPerSample;
        info.m_BlockAlign       = pcmWaveFormat.wf.nBlockAlign;
        info.m_channels         = pcmWaveFormat.wf.nChannels;
        info.m_formatType       = pcmWaveFormat.wf.wFormatTag;
        info.m_sampleRate       = pcmWaveFormat.wf.nSamplesPerSec;
 
    }else{
            /// Read in length of extra bytes.
        WORD cbExtraBytes = 0L;
        if( mmioRead( m_hmmio, ( CHAR* )&cbExtraBytes, sizeof( WORD ) ) != sizeof( WORD ) ){
            gtLogWriter::printWarning( u"Can not read length of extra bytes." );
            delete source;
            return nullptr;
        }
            
 
        /*m_pwfx = ( WAVEFORMATEX* )new CHAR[ sizeof( WAVEFORMATEX ) + cbExtraBytes ];
        if( NULL == m_pwfx ){
            gtLogWriter::printWarning( u"Can not allocate memmory for WAVEFORMATEX" );
            return nullptr;
        }*/
 
            /// Copy the bytes from the pcm structure to the waveformatex structure
        //memcpy( m_pwfx, &pcmWaveFormat, sizeof( pcmWaveFormat ) );
        //m_pwfx->cbSize = cbExtraBytes;
        info.m_AvgBytesPerSec   = pcmWaveFormat.wf.nAvgBytesPerSec;
        info.m_BitsPerSample    = pcmWaveFormat.wBitsPerSample;
        info.m_BlockAlign       = pcmWaveFormat.wf.nBlockAlign;
        info.m_channels         = pcmWaveFormat.wf.nChannels;
        info.m_formatType       = pcmWaveFormat.wf.wFormatTag;
        info.m_sampleRate       = pcmWaveFormat.wf.nSamplesPerSec;
 
            /// Now, read those extra bytes into the structure, if cbExtraAlloc != 0.
        u32 xb = sizeof(WAVEFORMATEX) + cbExtraBytes;
        if( mmioRead( m_hmmio, ( CHAR* )( ( ( BYTE* )&( xb ) ) + sizeof( WORD ) ),
                        cbExtraBytes ) != cbExtraBytes ){
            gtLogWriter::printWarning( u"Can not read extra bytes" );
            delete source;
            return nullptr;
        }
 
            /// Ascend the input file out of the 'fmt ' chunk.
        if( 0 != mmioAscend( m_hmmio, &ckIn, 0 ) ){
            gtLogWriter::printWarning( u"Can not ascend the input file out of the 'fmt ' chunk." );
            delete source;
            return nullptr;
        }
    }
 
        /// Seek to the data
    if( -1 == mmioSeek( m_hmmio, m_ckRiff.dwDataOffset + sizeof( FOURCC ),
                        SEEK_SET ) ){
        gtLogWriter::printWarning( u"Can not seek the data" );
        delete source;
        return nullptr;
    }
 
        /// Search the input file for the 'data' chunk.
    m_ck.ckid = mmioFOURCC( 'd', 'a', 't', 'a' );
    if( 0 != mmioDescend( m_hmmio, &m_ck, &m_ckRiff, MMIO_FINDCHUNK ) ){
        gtLogWriter::printWarning( u"Can not seek the data" );
        delete source;
        return nullptr;
    }
 
    source->allocate( m_ck.cksize );
 
    if( !source->getData() ){
        gtLogWriter::printWarning( u"Can not allocate memmory for audio source" );
        delete source;
        return nullptr;
    }
 
 
    /// READ
 
        /// current status of m_hmmio
    MMIOINFO mmioinfoIn;
 
    if( 0 != mmioGetInfo( m_hmmio, &mmioinfoIn, 0 ) ){
        gtLogWriter::printWarning( u"Can not get MMIOINFO" );
        delete source;
        return nullptr;
    }
 
    for( DWORD cT = 0; cT < m_ck.cksize; cT++ ){
        // Copy the bytes from the io to the buffer.
        if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ){
            if( 0 != mmioAdvance( m_hmmio, &mmioinfoIn, MMIO_READ ) ){
                gtLogWriter::printWarning( u"mmioAdvance fail" );
                delete source;
                return nullptr;
            }
 
            if( mmioinfoIn.pchNext == mmioinfoIn.pchEndRead ){
                gtLogWriter::printWarning( u"mmioinfoIn.pchNext" );
                delete source;
                return nullptr;
            }
        }
 
            /// Actual copy.
        *( ( BYTE* )source->getData() + cT ) = *( ( BYTE* )mmioinfoIn.pchNext );
        mmioinfoIn.pchNext++;
    }
 
    if( 0 != mmioSetInfo( m_hmmio, &mmioinfoIn, 0 ) ){
        gtLogWriter::printWarning( u"mmioSetInfo fail" );
        delete source;
        return nullptr;
    }
 
    source->setInfo( info );
 
    source->updateData();
 
    return source;
}
Загуглив на тему загрузки wav файла быстро нашел статью о нём.
На осное неё сделал новую версию.

wav файл поделён на 2 части. Заголовок и raw данные (значит, что данные не закодированы, и сразу пойдут на воспроизведение)

Заголовок состоит из
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct WaveHeader{
    u32 chunkId;        //"RIFF"
    u32 chunkSize;      //file size - 8bytes
    u32 format;         //"WAVE"-be (0x57415645)
    u32 subchunk1Id;    //"fmt"-be (0x666d7420)
    u32 subchunk1Size;  // 16 - PCM
    u16 audioFormat;    // 1  - PCM
    u16 numChannels;    // 1 - mono, 2 - stereo
    u32 sampleRate;     // 44100
    u32 byteRate;       // bytes in sec
    u16 blockAlign;     //
    u16 bitsPerSample;  // 16 bit
    u32 subchunk2Id;    //"data"-be (0x64617461)
    u32 subchunk2Size;  //data size
};
В общем то, главное знать audioFormat - для обычного сырого wav это должно быть 1.
Если что-то иное то вероятно формат не PCM или он как то зашифроан.

На XAudio2 проверил только 8/16 бит моно/стерео.

Если битность более 16 то выдаю ошибку(под вопросом). Вполне возможно что и такие форматы он играет.

subchunk2Size - размер `raw` данных - самого звука.

Размер структуры - 44 байта.
Читаем заголовок, проверяем, выделяем память под звук, читаем его.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
bool Wave::getInfo( gtAudioSourceInfo& info ){
 
    m_file = util::openFileForReadBinShared( m_fileName ).data();
 
    if( !m_file ){
        gtLogWriter::printWarning( u"Can not open file." );
        return false;
    }
 
    u32 bytesRead = m_file->read( (u8*)&m_header, sizeof(m_header) );
    m_file->release();
    m_file = nullptr;
 
    if( bytesRead != 44u ){
        gtLogWriter::printWarning( u"Can not read file. Bad header." );
        return false;
    }
 
    /*if( m_header.bitsPerSample > 16u ){
        gtLogWriter::printWarning( u"Unsupported bit depth." );
        return false;
    }*/
 
    if( m_header.audioFormat != 1u ){
        gtLogWriter::printWarning( u"Unsupported wav format." );
        return false;
    }
 
    info.m_bytesPerSec  = m_header.byteRate;
    info.m_bitsPerSample= m_header.bitsPerSample;
    info.m_blockAlign   = m_header.blockAlign;
    info.m_channels     = m_header.numChannels;
    info.m_formatType   = m_header.audioFormat;
    info.m_sampleRate   = m_header.sampleRate;
 
    return true;
}
 
gtAudioSourceImpl* Wave::read( gtAudioSourceInfo info ){
 
    gtAudioSourceImpl* source = new gtAudioSourceImpl;
    if( !source ) return nullptr;
 
    source->allocate( m_header.subchunk2Size );
    if( !source->getData() ){
        gtLogWriter::printWarning( u"Can not allocate memmory for audio source" );
        delete source;
        return nullptr;
    }
 
    m_file = util::openFileForReadBinShared( m_fileName ).data();
    if( !m_file ){
        gtLogWriter::printWarning( u"Can not open file." );
        delete source;
        return nullptr;
    }
 
    /*по сути это 44*/
    u32 offset = m_file->size() - m_header.subchunk2Size;
 
    m_file->seek( offset, gtFile::SeekPos::ESP_BEGIN );
    m_file->read( source->getData(), source->getDataSize() );
    m_file->release();
    m_file = nullptr;
 
    source->setInfo( info );
 
    source->updateData();
 
    return source;
}
Размещено в Игровой движок
Просмотров 353 Комментарии 0
Всего комментариев 0

Комментарии

 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru