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

Пишу игровой движок на C++. 035. Чтение .ogg и потоковое воспроизведение.

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

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

Пишу игровой движок на C++. 035. Чтение .ogg и потоковое воспроизведение.

Запись от 532235 размещена 02.02.2018 в 12:38

.ogg всего лишь контейнер для хранения аудио. Сама информация о звуке может находиться в разных форматах, закодированы разными кодеками.
Но обычно в .ogg используют кодек vorbis. Да и ogg и vorbis созданы одной командой.
По этому будет считаться - стандартная загрузка .ogg, это загрузка ogg-vorbis.

Необходимы библиотеки libogg и libvorbis.

Первая функция будет для получения информации. Я не понял как получить битность (8-16 и т.д.), да и думаю не нужно.
Стерео/моно работает вплоть до 500кб в сек.

По умолчанию, vorbisfile открывает файл посредством FILE.
Для Windows настоятельно не рекомендуют использовать этот способ. Существует возможность указать свои коллбэк функции на чтение, tell и seek.

Для моей ситуации это только в плюс, так как средствами Windows я могу открыть файл в режиме общего чтения (когда 2 и более программы могут открыть один и тот же файл).
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
size_t  readOgg( void *ptr, size_t size, size_t nmemb, void *datasource ){
    gtFile * file = (gtFile*)datasource;
    auto bytes = file->read( (u8*)ptr, size * nmemb );
    return bytes;
}
 
int seekOgg( void *datasource, ogg_int64_t offset, int whence ){
    gtFile * file = (gtFile*)datasource;
 
    gtFile::SeekPos pos;
    switch( whence ){
        case SEEK_CUR:
        pos = gtFile::SeekPos::ESP_CURRENT;
        break;
        case SEEK_SET:
        pos = gtFile::SeekPos::ESP_BEGIN;
        break;
        case SEEK_END:
        pos = gtFile::SeekPos::ESP_END;
        break;
    }
 
    file->seek( offset, pos );
    DWORD newpos = (DWORD)file->tell();
 
    if( newpos == INVALID_SET_FILE_POINTER ){
        DWORD error = GetLastError();
        return -1;
    }
 
    return newpos;
}
 
long tellOgg( void *datasource ){
    gtFile * file = (gtFile*)datasource;
    DWORD newpos = (DWORD)file->tell();
 
    if( newpos == INVALID_SET_FILE_POINTER ){
        DWORD error = GetLastError();
        return -1;
    }
    return newpos;
}
Теперь можно открывать файл и получать информацию.
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
bool Ogg::getInfo( gtAudioSourceInfo& info ){
    vorbis_info *pInfo;
    OggVorbis_File oggFile;
 
    ov_callbacks callbacks;
    callbacks.read_func =   readOgg;
    callbacks.seek_func =   seekOgg;
    callbacks.tell_func =   tellOgg;
    callbacks.close_func=   0;
 
    gtFile_t file = util::openFileForReadBinShared( m_fileName );
    if( ov_open_callbacks( file.data(), &oggFile, NULL, 0, callbacks ) < 0){
        gtLogWriter::printWarning( u"Can not open file for stream." );
        return false;
    }
 
    pInfo = ov_info(&oggFile, -1);
    
    info.m_formatType       = 1;
    info.m_channels         = pInfo->channels;
    info.m_sampleRate       = pInfo->rate;
    info.m_bytesPerSec      = pInfo->bitrate_nominal;
    info.m_blockAlign       = 4;
    info.m_bitsPerSample    = 16;
 
    m_time = ov_time_total( &oggFile, -1 ); /*Время аудио в секундах*/
 
    ov_clear(&oggFile);
 
    m_info = info;
 
    return true;
}
Теперь нужно уметь загрузить звук в память.
Как я понял, такой функции у libvorbis нет, в документации сказано что ov_read работает в цикле.
Да и с другой стороны, ogg нужны для длинных аудио, чтобы воспроизводить их в потокоом режиме, и читать всё сразу нет нужды.
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
gtAudioSourceImpl* Ogg::read( gtAudioSourceInfo info ){
    int endian = 0;
    int bitStream;
    long bytes;
    char array[BUFFER_SIZE];
    OggVorbis_File oggFile;
 
    ov_callbacks callbacks;
    callbacks.read_func =   readOgg;
    callbacks.seek_func =   seekOgg;
    callbacks.tell_func =   tellOgg;
    callbacks.close_func=   0;
 
    gtFile_t file = util::openFileForReadBinShared( m_fileName );
    if( ov_open_callbacks( file.data(), &oggFile, NULL, 0, callbacks ) < 0){
        gtLogWriter::printWarning( u"Can not open file for stream." );
        return false;
    }
    
    auto total = ov_raw_total( &oggFile, -1 );
 
    std::vector<u8> buffer;
    do{
        bytes = ov_read(&oggFile, array, BUFFER_SIZE, endian, 2, 1, &bitStream);
    
        buffer.insert(buffer.end(), array, array + bytes);
 
    }while( bytes > 0 );
 
    auto sz = buffer.size();
 
    ov_clear(&oggFile);
 
    gtAudioSourceImpl* source = new gtAudioSourceImpl;
    if( !source ) return nullptr;
 
    source->allocate( sz );
 
    memcpy( source->getData(), buffer.data(), sz );
 
    source->setInfo( info );
    source->updateData();
    source->m_time = m_time;
 
    return source;
}


Функция работающая в другом потоке для потокового воспроизведения выглядит так.
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
void    OggStreamFunc( void * arg ){
 
    OggStream*  args = (OggStream*)arg;
    
    *args->state = gtAudioState::play;
    auto * mainSystem = gtMainSystem::getInstance();
 
    u8 currentDiskReadBuffer = 0u;
 
    gtFile_t file = util::openFileForReadBinShared( args->filePath );
    
    ov_callbacks callbacks;
    callbacks.read_func =   readOgg;
    callbacks.seek_func =   seekOgg;
    callbacks.tell_func =   tellOgg;
    callbacks.close_func=   0;
 
    int endian = 0;
    int bitStream;
    long bytes;
    
    char buffers[MAX_BUFFER_COUNT][STREAMING_BUFFER_SIZE];
 
    OggVorbis_File oggFile;
    if( ov_open_callbacks( file.data(), &oggFile, NULL, 0, callbacks ) < 0){
        gtLogWriter::printWarning( u"Can not open file for stream." );
        return;
    }
 
    do{
        if( !mainSystem->isRun() ) break; /*если выход из программы то выход из цикла*/
 
        args->sourceVoice->Start( 0, XAUDIO2_COMMIT_ALL );
 
        ov_time_seek( &oggFile, *args->currentPosition );
 
        do{
            if( !mainSystem->isRun() ) break;
            
            bytes = ov_read(&oggFile, buffers[ currentDiskReadBuffer ], STREAMING_BUFFER_SIZE, endian, 2, 1, &bitStream);
    
            if( !bytes )
                break;
 
            XAUDIO2_VOICE_STATE state;
            for(; ; ){
                args->sourceVoice->GetState( &state );
                if( state.BuffersQueued < MAX_BUFFER_COUNT - 1 )
                    break;
            }
 
            XAUDIO2_BUFFER buf = {0};
            buf.AudioBytes = bytes;
            buf.pAudioData = (BYTE*)buffers[currentDiskReadBuffer];
            /*if( *args->currentPosition >= args->waveLength )
                buf.Flags = XAUDIO2_END_OF_STREAM;*/
 
            args->sourceVoice->SubmitSourceBuffer( &buf );
 
            if( *args->command != PlayBackCommand::PBC_NONE )
                break;
            *args->currentPosition = ov_time_tell( &oggFile );
 
            currentDiskReadBuffer++;
            currentDiskReadBuffer %= MAX_BUFFER_COUNT;
 
        }while( bytes > 0 );
 
        args->sourceVoice->Stop();
        args->sourceVoice->FlushSourceBuffers();
 
        if( *args->command == PlayBackCommand::PBC_SETPOS){
            *args->command = PlayBackCommand::PBC_NONE;
            continue;
        }
 
 
        if( *args->command == PlayBackCommand::PBC_PAUSE){
            *args->state            =   gtAudioState::pause;
            ov_clear(&oggFile);
            return;
        }else if( *args->command == PlayBackCommand::PBC_STOP){
            *args->currentPosition  = 0;
            *args->state            =   gtAudioState::stop;
            ov_clear(&oggFile);
            return;
        }
        *args->currentPosition  = 0;
 
    }while( *args->isLoop == 1 );
}

Функция запуска и завершения потока
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
bool    Ogg::prepareToStreaming( 
    IXAudio2SourceVoice*    sourceVoice,
    gtAudioState            *state){
    
    m_stream.filePath   =   m_fileName;
    m_stream.sourceVoice=   sourceVoice;
    m_stream.state      =   state;
    m_stream.command    =   &m_playBackCommand;
    m_stream.isLoop     =   &m_isLoop;
    m_stream.currentPosition    =   &m_position;
 
 
    if( !m_thread )
        m_thread = gtMainSystem::getInstance()->createThread();
    m_thread->join();/*это строка мб и не нужна. подстраховка*/
    m_thread->start( (gtThread::StartFunction)OggStreamFunc, (void*)&m_stream );
 
    return true;
}
void    Ogg::closeStream( void ){
    m_playBackCommand = PlayBackCommand::PBC_STOP;
    if( m_thread ){
        m_thread->join();/*тоже подстраховка. Вызывается автоматически в деструкторе.*/
        m_thread->release();
        m_thread = nullptr;
    }
}
Перемотка аудио простая. Значение от 0 до 1 переводится во время. Можно было подшаманить и вычислять sample rate (44100 = 1 секунда если sampleRate=44100)
C++
1
2
3
4
5
6
7
8
void    Ogg::setPos( f32 p ){
    m_position = p * m_time;
    m_playBackCommand   =   PlayBackCommand::PBC_SETPOS;
}
 
f32     Ogg::getPos( void ){
    return m_position * (1.0 / m_time);
}
----------------------------------
Пример
C++
1
2
3
4
5
6
7
8
9
gtAudioSource* audioSource = audioSystem->loadAudioSource( u"../media/escapade.wav" );
gtPtr_t( gtAudioObject, audio, audioSystem->createAudioObject( audioSource ) );
audio->play();
 
gtPtr_t( gtAudioStream, stream, audioSystem->createStream( u"C:/1.ogg" ) );
if( stream.data() ){
    stream->play();
    stream->setLoop( true );
}
Размещено в Игровой движок
Просмотров 287 Комментарии 0
Всего комментариев 0

Комментарии

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