mirichi氏のSoundTestを利用してoggファイルのストリーム再生をテスト。原典との違いはOggVorbisへのバインディングをfiddleで書いたこと。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
=begin | |
module OggVorbisはoggやvorbisにあるヘッダーファイルからコピペ書き換えした。 | |
mirichi氏のSoundTestクラスを使う必要がある。 | |
http://d.hatena.ne.jp/mirichi/20150103/p1 | |
これをコンパイルしたり、必要なdllを用意、パスを通すのは各自で行う。 | |
=end | |
=begin | |
/******************************************************************** | |
* * | |
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * | |
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * | |
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * | |
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * | |
* * | |
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * | |
* by the Xiph.Org Foundation http://www.xiph.org/ * | |
* * | |
******************************************************************** | |
=end | |
require "fiddle/import" | |
require 'fiddle/types' | |
module OggVorbis | |
extend Fiddle::Importer | |
dlload "libvorbisfile-3.dll" | |
# from ogg/os_types.h | |
typealias "ogg_int16_t", "short" | |
typealias "ogg_uint16_t", "unsigned short" | |
typealias "ogg_int32_t", "int" | |
typealias "ogg_uint32_t", "unsigned int" | |
typealias "ogg_int64_t", "long long" | |
typealias "ogg_uint64_t", "unsigned long long" | |
ogg_iovec_t = [ | |
"void *iov_base", | |
"size_t iov_len" | |
] | |
oggpack_buffer = [ | |
"long endbyte", | |
"int endbit", | |
"unsigned char *buffer", | |
"unsigned char *ptr", | |
"long storage" | |
] | |
ogg_page = [ | |
"unsigned char *header", | |
"long header_len", | |
"unsigned char *body", | |
"long body_len" | |
] | |
ogg_stream_state = [ | |
"unsigned char *body_data", | |
"long body_storage", | |
"long body_fill", | |
"long body_returned", | |
"int *lacing_vals", | |
"long lacing_storage", | |
"long lacing_fill", | |
"long lacing_packet", | |
"long lacing_returned", | |
"unsigned char header[282]", | |
"int header_fill", | |
"int e_o_s", | |
"int b_o_s", | |
"long serialno", | |
"long pageno", | |
"ogg_int64_t packetno", | |
"ogg_int64_t granulepos" | |
] | |
ogg_packet = [ | |
"unsigned char *packet", | |
"long bytes", | |
"long b_o_s", | |
"long e_o_s", | |
"ogg_int64_t granulepos", | |
"ogg_int64_t packetno" | |
] | |
ogg_sync_state = [ | |
"unsigned char *data", | |
"int storage", | |
"int fill", | |
"int returned", | |
"int unsynced", | |
"int headerbytes", | |
"int bodybytes" | |
] | |
# vorbis/codec.h | |
vorbis_info = [ | |
"int version", | |
"int channels", | |
"long rate", | |
"long bitrate_upper", | |
"long bitrate_nominal", | |
"long bitrate_lower", | |
"long bitrate_window", | |
"void *codec_setup" | |
] | |
VorbisInfo = struct(vorbis_info) | |
vorbis_dsp_state = [ | |
"int analysisp", | |
"void *vi", # vorbis_info *vi | |
"float **pcm", | |
"float **pcmret", | |
"int pcm_storage", | |
"int pcm_current", | |
"int pcm_returned", | |
"int preextrapolate", | |
"int eofflag", | |
"long lW", | |
"long W", | |
"long nW", | |
"long centerW", | |
"ogg_int64_t granulepos", | |
"ogg_int64_t sequence", | |
"ogg_int64_t glue_bits", | |
"ogg_int64_t time_bits", | |
"ogg_int64_t floor_bits", | |
"ogg_int64_t res_bits", | |
"void *backend_state" | |
] | |
vorbis_block = [ | |
"float **pcm", | |
oggpack_buffer, # "oggpack_buffer opb", | |
"long lW", | |
"long W", | |
"long nW", | |
"int pcmend", | |
"int mode", | |
"int eofflag", | |
"ogg_int64_t granulepos", | |
"ogg_int64_t sequence", | |
"void *vd", #vorbis_dsp_state *vd | |
"void *localstore", | |
"long localtop", | |
"long localalloc", | |
"long totaluse", | |
"void *reap", # struct alloc_chain *reap | |
"long glue_bits", | |
"long time_bits", | |
"long floor_bits", | |
"long res_bits", | |
"void *internal" | |
] | |
alloc_chain = [ | |
"void *ptr", | |
"void *next" # "struct alloc_chain *next" | |
] | |
vorbis_comment = [ | |
"char **user_comments", | |
"int *comment_lengths", | |
"int comments", | |
"char *vendor" | |
] | |
ov_callbacks = [ | |
"size_t *read_func", #size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); | |
"int *seek_func", #int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); | |
"int *close_func", #int (*close_func) (void *datasource); | |
"long *tell_func" #long (*tell_func) (void *datasource); | |
] | |
OggVorbis_File = struct([ | |
"void *datasource", #/* Pointer to a FILE *, etc. */ | |
"int seekable", | |
"ogg_int64_t offset", | |
"ogg_int64_t end", | |
ogg_sync_state, #oy; | |
#/* If the FILE handle isn't seekable (eg, a pipe), only the current | |
#stream appears */ | |
"int links", | |
"ogg_int64_t *offsets", | |
"ogg_int64_t *dataoffsets", | |
"long *serialnos", | |
"ogg_int64_t *pcmlengths", #/* overloaded to maintain binary | |
#compatibility; x2 size, stores both | |
#beginning and end values */ | |
"void *vi", #vorbis_info *vi; | |
"void *vc", #vorbis_comment *vc; | |
#/* Decoding working state local storage */ | |
"ogg_int64_t pcm_offset", | |
"int ready_state", | |
"long current_serialno", | |
"int current_link", | |
"double bittrack", | |
"double samptrack", | |
ogg_stream_state, #os; #/* take physical pages, weld into a logical | |
#stream of packets */ | |
vorbis_dsp_state, #vd; #/* central working state for the packet->PCM decoder */ | |
vorbis_block, #vb; #/* local working space for packet->PCM decode */ | |
ov_callbacks, #callbacks; | |
].flatten!) | |
# from vorbisfile.h | |
extern "int ov_clear(OggVorbis_File*)" | |
extern "int ov_fopen(const char *path, OggVorbis_File*)" | |
extern "int ov_open(FILE*, OggVorbis_File*, const char *initial, long)" | |
#call_back: extern "int ov_open_callbacks(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks)" | |
extern "int ov_test(FILE*, OggVorbis_File*, const char *initial, long)" | |
#call_back: extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks); | |
extern "int ov_test_open(OggVorbis_File*)" | |
extern "long ov_bitrate (OggVorbis_File*, int)" | |
extern "long ov_bitrate_instant(OggVorbis_File*)" | |
extern "long ov_streams (OggVorbis_File*)" | |
extern "long ov_seekable (OggVorbis_File*)" | |
extern "long ov_serialnumber (OggVorbis_File*, int)" | |
extern "ogg_int64_t ov_raw_total (OggVorbis_File* ,int)" | |
extern "ogg_int64_t ov_pcm_total (OggVorbis_File* ,int)" | |
extern "double ov_time_total(OggVorbis_File* ,int)" | |
extern "int ov_raw_seek (OggVorbis_File*, ogg_int64_t)" | |
extern "int ov_pcm_seek (OggVorbis_File*, ogg_int64_t)" | |
extern "int ov_pcm_seek_page (OggVorbis_File*, ogg_int64_t)" | |
extern "int ov_time_seek (OggVorbis_File*, double)" | |
extern "int ov_time_seek_page(OggVorbis_File*, double)" | |
extern "int ov_raw_seek_lap (OggVorbis_File*, ogg_int64_t)" | |
extern "int ov_pcm_seek_lap (OggVorbis_File*, ogg_int64_t)" | |
extern "int ov_pcm_seek_page_lap (OggVorbis_File*, ogg_int64_t)" | |
extern "int ov_time_seek_lap (OggVorbis_File*, double)" | |
extern "int ov_time_seek_page_lap(OggVorbis_File*, double)" | |
extern "ogg_int64_t ov_raw_tell (OggVorbis_File*)" | |
extern "ogg_int64_t ov_pcm_tell (OggVorbis_File*)" | |
extern "double ov_time_tell(OggVorbis_File*)" | |
extern "vorbis_info *ov_info (OggVorbis_File*, int)" | |
extern "vorbis_comment *ov_comment(OggVorbis_File*, int)" | |
extern "long ov_read_float (OggVorbis_File*, float ***pcm_channels, int, int*)" | |
extern "long ov_read_filter(OggVorbis_File*, char*, int, int, int, int, int*, void*, void*)" | |
extern "long ov_read (OggVorbis_File*, char*, int, int, int, int, int*)" | |
extern "int ov_crosslap (OggVorbis_File*, OggVorbis_File*)" | |
extern "int ov_halfrate (OggVorbis_File*, int)" | |
extern "int ov_halfrate_p(OggVorbis_File*)" | |
end | |
# 自作クラス。GC対応とかがまだ学んでいないため放置。再生実験のためreadのみ実装。 | |
class CodecSound | |
def self.open() | |
end | |
def initialize(filepath, buffer_size: 4096) | |
@buffer = "\x00" * buffer_size | |
@codec = OggVorbis | |
@ogg_vorbis_file = OggVorbis::OggVorbis_File.malloc | |
@codec.ov_fopen(filepath, @ogg_vorbis_file) | |
@bit_depth = 16 | |
@endian = 0 | |
@signed = 1 | |
end | |
def close | |
@codec.ov_clear(@ogg_vorbis_file) | |
end | |
def info | |
OggVorbis::VorbisInfo.new(@codec.ov_info(@ogg_vorbis_file, -1)) | |
end | |
# | |
# この実装では `writebuf_str': lock error (SoundTestError)がでる。 | |
# 実装の目的はRubyのIO#readと同等にしたいということ。 | |
# | |
def _read(length = nil) | |
data = "" | |
while length.nil? || data.size < length do | |
len = length - data.size > @buffer.size ? @buffer.size : length | |
read_size = @codec.ov_read(@ogg_vorbis_file, @buffer, len, @endian, @bit_depth / 8, @signed, nil) | |
break if read_size.zero? | |
read_data = @buffer[0, read_size] | |
data << read_data | |
end | |
data | |
end | |
# | |
# エラーの出ない簡易実装。 | |
# | |
def read(length) | |
read_size = @codec.ov_read(@ogg_vorbis_file, @buffer, length, @endian, @bit_depth / 8, @signed, nil) | |
read_data = @buffer[0, read_size] | |
end | |
#def read_float | |
#def read_filter | |
end | |
# ここからmirichi氏のコードのコピペ改良 | |
require 'dxruby' | |
require_relative 'soundtest' | |
class OggStream < SoundTest | |
def initialize(filename) | |
#@ogg = SoundOgg.new(filename) # | |
@ogg = CodecSound.new(filename) # 自作クラスを使用 | |
super(@ogg.info.rate, @ogg.info.rate, 16, @ogg.info.channels) | |
@pos = 0 | |
@prc = Proc.new do |size| | |
buf = "" | |
reqsize = size | |
while(buf.size < size) do | |
tmp = @ogg.read(reqsize) | |
if tmp.size == 0 | |
break | |
end | |
reqsize -= tmp.size | |
buf.concat(tmp) | |
end | |
buf | |
end | |
self.writebuf_str(&@prc) # とりあえずバッファを埋める | |
end | |
def play | |
self.stop if @th | |
super(true) # ループ再生 | |
@th = Thread.new do | |
loop do | |
begin | |
break if self.wait | |
self.writebuf_str(&@prc) | |
rescue | |
p $!, $@ | |
end | |
end | |
end | |
@th.priority = 1 | |
end | |
end | |
rs1 = OggStream.new("_your_music_file_.ogg") # 手持ちのoggファイルを指定する。 | |
rs1.play | |
Window.loop do | |
Window.draw_font(0, 0, Window.fps.to_i.to_s, Font.default) | |
Window.draw_line(0, 100, 639, 100, C_WHITE) | |
pos, playpos, writepos = rs1.getpos | |
Window.draw_font(668.0 * playpos / rs1.size - 12, 100, '↑', Font.default, color:C_GREEN) | |
Window.draw_font(668.0 * writepos / rs1.size - 12, 100, '↑', Font.default, color:C_BLUE) | |
Window.draw_font(668.0 * pos / rs1.size - 12, 100, '↑', Font.default, color:C_RED) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment