Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
mirichi氏のSoundTestを利用してoggファイルのストリーム再生をテスト。原典との違いはOggVorbisへのバインディングをfiddleで書いたこと。
=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