|
You Tubeを見ているとオーディオスペクトラム動画なるものを見かけることがあります。
これらの動画は専用のソフトや市販ソフトを使って作られているようです。
専用のソフト等には到底及びませんが、十進BASICでオーディオスペクトラム画像を書出します。
ここではGIFアニメではなく動画ファイル(mp4形式)として作成します。(画像サンプルを参照)
その補助ツール(メインツール!?)にffmpegを使用します。
ffmpeg.exeはコマンドラインアプリです。
https://www.ffmpeg.org
FFmpeg Buildsからwindows版 version 4.0 windows 64bit staticをダウンロードしました。
まず、WAVファイルを用意します。
WAVファイルをそのままで保存している方は少ないと思いますので
ないなら変換してWAVファイルを作成します。
想定しているWAVファイルは44.1kHzサンプリング 16bit 2チャンネルです。
この変換にffmpegが使用できます。コマンドプロンプトを起動して
mp3やflac,oggなどの音楽ファイルから
ffmpeg -i sample.mp3 sample.wav
ffmpeg -i sample.mp3 -acodec pcm_s16le -ar 44100 -ac 2 sample.wav (16bit pcm, 44.1kHz, 2channel)
mp4やmpg,flvなどの動画ファイルから
ffmpeg -i sample.flv -vn sample.wav (-vn videoなし)
ffmpeg --help とすると、簡単なヘルプが表示されます。
曲長として 4~5分程度を想定しています。ドライブ残量に気を付けてください。
5分の曲とすると
44100Hz * 16bit/8 * 2ch * 300秒 = 52920000 byte
フレームレートは30fpsを想定しています。300秒 * 30fps =9000枚の画像(jpg)を十進BASICで書き出します。
用意ができたら十進BASICを実行して、WAVファイルを読み込みます。
但し、全てのWAVファイルに対応しているわけではありません。
実行には曲長の数倍の時間がかかります。
出来上がった5桁の連番画像ファイルと音楽ファイルで、mp4ファイルに変換します。
フレームレート30fps ビデオコーデックh264 オーディオコーデックaac を指定します。
ffmpeg -r 30 -i image%05d.jpg -i sample.wav -vcodec h264 -acodec aac sample.mp4
ffmpeg -framerate 30 -i image%05d.jpg -i sample.wav -r 30 -vcodec libx264 -acodec aac sample.mp4
また、設定しだいでmpg,flvやwebmなどが作成できます。
大量に画像を作成しますので動作をテストモードにしています。
変数TESTを書き換えてください。
実際に動画作成してみました。
曲長 237.0989569161秒 画像 7112枚を出力 画像1枚あたり多くて100kB前後 少なくて20kB前後
mp4に変換すると、およそ30MB程のmp4動画ができました。
(チャン! チャン!)
OPTION ARITHMETIC NATIVE
OPTION BASE 0
OPTION CHARACTER BYTE
DIM A$(20)
LET XSIZE=800 !'画像サイズ
LET YSIZE=400
LET FRAMERATE=30 !'動画フレームレート 30fps
LET DISPLAYMODE=0
!'INPUT PROMPT "DISPLAY MODE (0 - 9)=":DISPLAYMODE
LET TEST=0 !'画像の書出し 0以外にすると画像書出し。
LET SCALE=YSIZE/XSIZE
SET TEXT BACKGROUND "OPAQUE"
FILE GETNAME F$,"WAVファイル|*.WAV"
IF F$="" THEN STOP
FILE SPLITNAME (F$) PATH$, NAME$, EXT$
OPEN #1:NAME F$,ACCESS INPUT !'wavファイル読み込み
FOR I=1 TO 12
CHARACTER INPUT #1:A$(I)
NEXT I
IF A$(1)&A$(2)&A$(3)&A$(4)<>"RIFF" THEN
PRINT "WAVファイルではありません"
CLOSE #1
STOP
END IF
LET WAVEFILESIZE=CVL(A$(5)&A$(6)&A$(7)&A$(8))
IF A$(9)&A$(10)&A$(11)&A$(12)<>"WAVE" THEN
PRINT "WAVファイルではありません"
CLOSE #1
STOP
END IF
DO
FOR I=1 TO 4
CHARACTER INPUT #1:A$(I)
NEXT I
SELECT CASE A$(1)&A$(2)&A$(3)&A$(4)
CASE "fmt "
FOR I=1 TO 4
CHARACTER INPUT #1:A$(I)
NEXT I
LET HEADERSIZE=CVL(A$(1)&A$(2)&A$(3)&A$(4))
FOR I=1 TO HEADERSIZE
CHARACTER INPUT #1:A$(I)
NEXT I
LET WAVETYPE=CVI(A$(1)&A$(2))
IF WAVETYPE<>1 THEN
PRINT "対応していません"
CLOSE #1
STOP
END IF
LET CHANNEL=CVI(A$(3)&A$(4))
LET SAMPLINGFREQ=CVL(A$(5)&A$(6)&A$(7)&A$(8))
LET DATARATE=CVL(A$(9)&A$(10)&A$(11)&A$(12))
LET SAMPLESIZE=CVI(A$(13)&A$(14))
LET SAMPLEBIT=CVI(A$(15)&A$(16))
CASE "data"
FOR I=1 TO 4
CHARACTER INPUT #1:A$(I)
NEXT I
LET PCMSIZE=CVL(A$(1)&A$(2)&A$(3)&A$(4))
LET SECOND=PCMSIZE/DATARATE
!' LET NUM=INT(SAMPLINGFREQ*SECOND)
LET NUM=PCMSIZE/SAMPLESIZE
EXIT DO
CASE "fact"
FOR I=1 TO 8
CHARACTER INPUT #1:A$(I)
NEXT I
!' LET SIZE=CVL(A$(1)&A$(2)&A$(3)&A$(4))
!' LET SAMPLE=CVL(A$(5)&A$(6)&A$(7)&A$(8))
CASE "LIST"
FOR I=1 TO 4
CHARACTER INPUT #1:A$(I)
NEXT I
LET SIZE=CVL(A$(1)&A$(2)&A$(3)&A$(4))
FOR I=1 TO SIZE
CHARACTER INPUT #1:DMY$ !'空読み
NEXT I
CASE ELSE
PRINT "対応していません"
CLOSE #1
STOP
END SELECT
LOOP
PRINT "ファイルサイズ ";WAVEFILESIZE;"Byte" !' データ表示
PRINT "WAVEタイプ ";WAVETYPE
PRINT "チャンネル数 ";CHANNEL;"ch"
PRINT "サンプリング周波数 ";SAMPLINGFREQ;"Hz"
PRINT "データレート ";DATARATE;"Byte"
PRINT "ブロックサイズ ";SAMPLESIZE;"Byte"
PRINT "サンプリングビット ";SAMPLEBIT;"bit"
PRINT "PCMサイズ ";PCMSIZE;"Byte"
PRINT "データ数 ";PCMSIZE/SAMPLESIZE;INT(SAMPLINGFREQ*SECOND)
PRINT "時間 ";SECOND;"秒"
PRINT "描画モード ";DISPLAYMODE
PRINT "出力画像 ";INT(SECOND*FRAMERATE);"枚"
SET POINT STYLE 1
SET COLOR MODE "REGULAR"
SET COLOR MIX(0) 0,0,0
SET COLOR MIX(1) 0,0,1
SET COLOR MIX(2) 1,0,0
SET COLOR MIX(3) 1,0,1
SET COLOR MIX(4) 0,1,0
SET COLOR MIX(5) 0,1,1
SET COLOR MIX(6) 1,1,0
SET COLOR MIX(7) 1,1,1
SET BITMAP SIZE XSIZE,YSIZE
LET N=INT(SAMPLINGFREQ/FRAMERATE) !'1フレームあたりのポイント数
LET NN=2^INT(LOG2(N)+1) !'FFTポイント数
DIM RMAX(NN),LR(NN),RR(NN),FR(NN),FI(NN)
LET B$=" "
FOR K=0 TO NUM-1
FOR CH=1 TO CHANNEL
FOR J=1 TO SAMPLEBIT/8
CHARACTER INPUT #1:B$(J:J)
NEXT J
IF SAMPLEBIT=8 THEN LET DAT=ORD(B$(1:1))-128
IF SAMPLEBIT=16 THEN LET DAT=CVI(B$)
LET J=MOD(K,N)
SELECT CASE CH
CASE 1
LET LR(J)=DAT
CASE 2
LET RR(J)=DAT
CASE ELSE
END SELECT
NEXT CH
IF CHANNEL=1 THEN MAT RR=LR !'モノラルならコピー
IF K>0 AND J=0 THEN !'1フレーム描画 30フレームで1秒分
LET COUNT=COUNT+1
SET DRAW MODE HIDDEN !'ちらつき防止
SELECT CASE DISPLAYMODE !'画面表示
CASE 0
CALL DISPLAY0
CASE 1
CALL DISPLAY1
CASE 2
CALL DISPLAY2
CASE 3
CALL DISPLAY3
CASE 4
CALL DISPLAY4
CASE 5
CALL DISPLAY5
CASE 6
CALL DISPLAY6
CASE 7
CALL DISPLAY7
CASE 8
CALL DISPLAY8
CASE 9
CALL DISPLAY9
END SELECT
SET DRAW MODE EXPLICIT
IF TEST<>0 THEN GSAVE PATH$&"image"&RIGHT$("00000"&STR$(COUNT),5)&".jpg" !'画像書出し
END IF
NEXT K
CLOSE #1
PRINT "ffmpeg -r";FRAMERATE;"-i ";CHR$(34);PATH$;"image%05d.jpg";CHR$(34);" -i ";CHR$(34);F$;CHR$(34);" -vcodec h264 -b:v 1000k -acodec aac -b:a 128k ";CHR$(34);PATH$;NAME$;".mp4";CHR$(34)
SUB DISPLAY1
CALL GCLEAR
CALL TIMER(.9,1,0,.05,2,12) !'タイマー
CALL WAVE(RR,.51,1,.05,.95,5) !'右チャンネル(右)
CALL WAVE(LR,0,.49,.05,.95,5) !'左チャンネル(左)
END SUB
SUB DISPLAY2
CALL GCLEAR
CALL TIMER(.9,1,0,.05,2,12) !'タイマー
CALL WAVE2(LR,0,1,.6,.9,4) !'左チャンネル(上)
CALL WAVE2(RR,0,1,.1,.4,4) !'右チャンネル(下)
END SUB
SUB DISPLAY3
CALL GCLEAR
CALL TIMER(.9,1,0,.05,2,12)
CALL FREQ(LR,0,1,.6,.9,5,2) !'左チャンネル(上)
CALL FREQ(RR,0,1,.1,.4,5,2) !'右チャンネル(下)
END SUB
SUB DISPLAY4
CALL GCLEAR
CALL POWER(LR,.05,.48,.05,.95,4,2,8,"MODE2","RIGHT") !'左チャンネル(左)
CALL POWER(RR,.52,.95,.05,.95,4,2,8,"MODE2","LEFT") !'右チャンネル(右)
END SUB
SUB DISPLAY5
CALL GCLEAR
CALL WAVE3(LR,0,1,.6,.9,6,2) !'左チャンネル(上)
CALL WAVE3(RR,0,1,.1,.4,6,2) !'右チャンネル(下)
END SUB
SUB DISPLAY6
CALL GCLEAR
CALL VOLUMEBAR(LR,.1,.9,.7,.8,4,2,"MODE2","LEFT") !'左チャンネル(上)
CALL VOLUMEBAR(RR,.1,.9,.2,.3,4,2,"MODE2","LEFT") !'右チャンネル(下)
END SUB
SUB DISPLAY7
CALL GCLEAR
CALL VOLUMECIRCLE(LR,.05,.45,.1,.9,4,2) !'左チャンネル(左)
CALL VOLUMECIRCLE(RR,.55,.95,.1,.9,4,2) !'右チャンネル(右)
END SUB
SUB DISPLAY8
CALL GCLEAR
CALL XYPLOT(RR,.51,1,.05,.95,7) !'右チャンネル(右)
CALL XYPLOT(LR,0,.49,.05,.95,7) !'左チャンネル(左)
END SUB
SUB DISPLAY9
CALL GCLEAR
CALL POWERDISK(RR,.51,1,.05,.95,5,2,8,"MODE2") !'右チャンネル(右)
CALL POWERDISK(LR,0,.49,.05,.95,5,2,8,"MODE2") !'左チャンネル(左)
END SUB
SUB DISPLAY0
CALL GCLEAR
CALL TIMER(.9,1,0,.04,2,10)
CALL TIMEBAR(.1,.8,0,.04,3)
CALL VOLUMEBAR(LR,.02,.49,.05,.08,1,2,"MODE2","LEFT")
CALL POWER(LR,.02,.49,.1,.5,4,2,8,"MODE2","BOTTOM")
CALL FREQ(LR,.02,.49,.52,.75,6,2)
CALL WAVE(LR,.02,.49,.76,.99,5)
CALL VOLUMEBAR(RR,.51,.98,.05,.08,1,2,"MODE2","LEFT")
CALL POWER(RR,.51,.98,.1,.5,4,2,8,"MODE2","BOTTOM")
CALL FREQ(RR,.51,.98,.52,.75,6,2)
CALL WAVE(RR,.51,.98,.76,.99,5)
END SUB
SUB GCLEAR
SET VIEWPORT 0,1,0,SCALE
SET WINDOW 0,1,0,SCALE
SET AREA COLOR 7
PLOT AREA:0,0;1,0;1,SCALE;0,SCALE
END SUB
SUB TIMER(XS,XE,YS,YE,COL,HEIGHT)
SET VIEWPORT XS,XE,YS*SCALE,YE*SCALE !'VIEWPORTの領域を長方形でも 左下(0,0) 右上(1,1)とする
SET WINDOW XS,XE,YS,YE
SET TEXT FONT "",HEIGHT
SET TEXT COLOR COL
PLOT TEXT ,AT XS,YS:RIGHT$("0"&STR$(INT(COUNT/FRAMERATE/60)),2)&":"&RIGHT$("0"&STR$(MOD(INT(COUNT/FRAMERATE),60)),2)&"."&RIGHT$("0"&STR$(MOD(COUNT,FRAMERATE)),2)
END SUB
SUB TIMEBAR(XS,XE,YS,YE,COL)
SET VIEWPORT XS,XE,YS*SCALE,YE*SCALE
LET XS=0
LET XE=NUM-1
LET YS=0
LET YE=1
SET WINDOW XS,XE,YS,YE
CALL BOXFULL(XS,YS,XE,YE,0)
CALL BOXFULL(0,0,K,1,COL)
END SUB
SUB WAVE(RR(),XS,XE,YS,YE,COL) !'波形
SET VIEWPORT XS,XE,YS*SCALE,YE*SCALE
LET XS=0
LET XE=N-1
LET YS=-2^(SAMPLEBIT-1)
LET YE=-YS
SET WINDOW XS,XE,YS,YE
CALL BOXFULL(XS,YS,XE,YE,0)
SET LINE COLOR COL
FOR I=0 TO N-1
PLOT LINES:I,RR(I);
NEXT I
END SUB
SUB WAVE2(RR(),XS,XE,YS,YE,COL)
SET VIEWPORT XS,XE,YS*SCALE,YE*SCALE
LET XS=0
LET XE=N-1
LET YS=-2^(SAMPLEBIT-1)
LET YE=-YS
SET WINDOW XS,XE,YS,YE
CALL BOXFULL(XS,YS,XE,YE,0)
SET LINE COLOR COL
FOR I=0 TO N-1
PLOT LINES:I,-RR(I);I,RR(I)
NEXT I
END SUB
SUB WAVE3(RR(),XS,XE,YS,YE,COL,MAXCOL)
SET VIEWPORT XS,XE,YS*SCALE,YE*SCALE
LET XS=0
LET XE=N-1
LET YE=2^(SAMPLEBIT-1)
LET YS=0
SET WINDOW XS,XE,YS,YE
CALL BOXFULL(XS,YS,XE,YE,0)
IF COUNT=1 OR MOD(COUNT,INT(FRAMERATE/2))=0 THEN !'フレームレートの半分で最大値クリア
MAT RMAX=ZER
END IF
SET LINE COLOR MAXCOL
FOR I=0 TO N-1
PLOT LINES:I,0;I,RMAX(I)
NEXT I
PLOT LINES
SET LINE COLOR COL
FOR I=0 TO N-1
LET R=ABS(RR(I))
LET RMAX(I)=MAX(RMAX(I),R)
PLOT LINES:I,0;I,R
NEXT I
END SUB
SUB FREQ(RR(),XS,XE,YS,YE,COL,MAXCOL) !'周波数
SET VIEWPORT XS,XE,YS*SCALE,YE*SCALE
LET XS=0
LET XE=NN/2-1
LET YS=0
LET YE=20*LOG10(2^(SAMPLEBIT-1))
SET WINDOW XS,XE,YS,YE
CALL BOXFULL(XS,YS,XE,YE,0)
IF COUNT=1 OR MOD(COUNT,INT(FRAMERATE/2))=0 THEN
MAT RMAX=ZER
END IF
FOR I=0 TO NN-1
LET FR(I)=RR(I)*HANNING(I,NN) !'ハニング窓関数を掛ける
NEXT I
MAT FI=ZER
CALL CDFT(2*NN,COS(PI/NN),SIN(PI/NN),FR,FI) !'FFT
SET LINE COLOR COL
FOR I=0 TO NN/2-1
LET R=10*LOG10(FR(I)^2+FI(I)^2+1)
LET RMAX(I)=MAX(RMAX(I),R)
PLOT LINES:I,R;
NEXT I
PLOT LINES
SET LINE COLOR MAXCOL
FOR I=0 TO NN/2-1
PLOT LINES:I,RMAX(I); !'最大値描画
NEXT I
END SUB
SUB POWER(RR(),XS,XE,YS,YE,COL,MAXCOL,LEV,MODE$,DIRECTION$) !'スペクトル
SET VIEWPORT XS,XE,YS*SCALE,YE*SCALE
LET P=10*LOG10(2^(SAMPLEBIT-1))
SELECT CASE DIRECTION$
CASE "BOTTOM"
LET YS=0
LET YE=P
LET XE=NN/2-1
LET XS=0
CASE "TOP"
LET YS=P
LET YE=0
LET XS=0
LET XE=NN/2-1
CASE "LEFT"
LET XS=0
LET XE=P
LET YS=0
LET YE=NN/2-1
CASE "RIGHT"
LET XS=P
LET XE=0
LET YS=0
LET YE=NN/2-1
END SELECT
SET WINDOW XS,XE,YS,YE
CALL BOXFULL(XS,YS,XE,YE,0)
FOR I=0 TO NN-1
LET FR(I)=RR(I)*HANNING(I,NN)
NEXT I
MAT FI=ZER
CALL CDFT(2*NN,COS(PI/NN),SIN(PI/NN),FR,FI)
LET T=NN/LEV/2
FOR I=0 TO NN/2-1 STEP T
LET R=0
FOR P=0 TO T-1
LET R=R+10*LOG10(FR(I+P)^2+FI(I+P)^2+1)
NEXT P
LET R=R/T !'平均値
SELECT CASE DIRECTION$
CASE "TOP","BOTTOM"
SELECT CASE MODE$
CASE "MODE1"
IF R>MAX(YS,YE)/32 THEN
FOR Y=0 TO R STEP MAX(YS,YE)/32
IF Y<MAX(YS,YE)*.8 THEN CALL BOXFULL(I,Y,T*3/4+I,Y+MAX(YS,YE)/32,COL) ELSE CALL BOXFULL(I,Y,T*3/4+I,Y+MAX(YS,YE)/32,MAXCOL)
NEXT Y
END IF
CASE "MODE2"
IF R>MAX(YS,YE)/16 THEN
FOR Y=0 TO R STEP MAX(YS,YE)/16
IF Y<MAX(YS,YE)*.8 THEN CALL BOXFULL(I,Y,T*3/4+I,Y+MAX(YS,YE)/16*.8,COL) ELSE CALL BOXFULL(I,Y,T*3/4+I,Y+MAX(YS,YE)/16*.8,MAXCOL)
NEXT Y
END IF
END SELECT
CASE "LEFT","RIGHT"
SELECT CASE MODE$
CASE "MODE1"
IF R>MAX(XS,XE)/32 THEN
FOR X=0 TO R STEP MAX(XS,XE)/32
IF X<MAX(XS,XE)*.8 THEN CALL BOXFULL(X,I,X+MAX(XS,XE)/32,T*3/4+I,COL) ELSE CALL BOXFULL(X,I,X+MAX(XS,XE)/32,T*3/4+I,MAXCOL)
NEXT X
END IF
CASE "MODE2"
IF R>MAX(XS,XE)/16 THEN
FOR X=0 TO R STEP MAX(XS,XE)/16
IF X<MAX(XS,XE)*.8 THEN CALL BOXFULL(X,I,X+MAX(XS,XE)/16*.8,T*3/4+I,COL) ELSE CALL BOXFULL(X,I,X+MAX(XS,XE)/16*.8,T*3/4+I,MAXCOL)
NEXT X
END IF
END SELECT
END SELECT
NEXT I
END SUB
続く
|
|