Sound file sample access (WAV or MP3)

General FreeBASIC programming questions.
Post Reply
Sisophon2001
Posts: 1706
Joined: May 27, 2005 6:34
Location: Cambodia, Thailand, Lao, Ireland etc.
Contact:

Sound file sample access (WAV or MP3)

Post by Sisophon2001 »

Hi,

Is there a convenient library to access individual samples from a sound file? Between WAV and MP3 there are so many sound formats it is discouraging to do from scratch. I had working code years ago, based on code by D.J. Peters, but I have lost it now.

In Python, this is what I want to do, but I don't know Python very well and would prefer to have a WIN-API program in FreeBASIC.

Code: Select all

# Make plots appear inline, set custom plotting style
import matplotlib.pyplot as plt
import numpy as np
from scipy.io import wavfile
from scipy import signal

rate, audio = wavfile.read('out.wav')

audio = np.mean(audio, axis=1)
M = 1024

freqs, times, Sx = signal.spectrogram(audio, fs=rate, window='hanning',
                                      nperseg=M, noverlap=M - 100,
                                      detrend=False, scaling='spectrum')

f, ax = plt.subplots(figsize=(10, 6))
ax.pcolormesh(times, freqs / 1000, 10 * np.log10(Sx), cmap='viridis')
ax.set_ylim([0,20])

ax.set_ylabel('Frequency [kHz]')
ax.set_xlabel('Time [s]')
plt.show()

#######################
import struct
startpos = 6.696
endpos = 7.592
lowfreq = 3
hifreq = 8
startpos = long(startpos * 441)
endpos = long(endpos * 441)
#lowfreq = lowfreq *
#hifreq = hifreq *
# need to learn how to calculate the frequency from 0 to 513.
handle = open('floats.bin', 'wb')
#handle.write(struct.pack('<i', startpos))
handle.write(Sx[:,startpos:endpos].tobytes())
# Write all
#handle.write(Sx[:,:].tobytes())

handle.close()
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: Sound file sample access (WAV or MP3)

Post by grindstone »

I don't know what exactly you want to do, but here's a snippet that normalizes a single wav - file (pcm or float). It's part of a program I use to batch - normalize / convert the wav - files of a whole directory. For the conversion it uses ffmpeg.

Code: Select all

#Include "..\fbm_RIFFheader.bas"
#Include "vbcompat.bi"

Const As Integer schwarz = 0, blau = 1, gruen = 2, zyan = 3, cyan = 3, rot = 4, magenta = 5, _
								 braun = 6, hellgrau = 7, grau = 8, hellblau = 9, hellgruen = 10, hellzyan = 11, _
								 hellcyan = 11, hellrot = 12, pink = 13, gelb = 14, weiss = 15

Dim As String g
Dim As String quelle, ziel
Dim As Integer laengemerken, keinedatei, header, dateilaenge, subch1, audioformat, kanaele, samplerate, _
               byterate, blockalign, bitspersample, subch2, blocklaenge, x, ma
Dim As LongInt samples, gesamtsamples, bloecke, rest
Dim As Single maxamplitude, dauer, sollwertpcm, sollwertfloat, links, rechts, verst, schwelle
Dim As Double geslinks, gesrechts
Dim As Short Ptr gpp
Dim As Single Ptr gpf 

Declare Sub maske
Declare Function vh (x As Single) As Single	
Declare Function db (x As Single) As Single
		
'initialize variables
quelle = "g:\RIFF\RMS Abgleichton.wav" 'source file
ziel = "g:\RIFF\mod.wav" 'destination file

sollwertpcm = -18 'desired value (in dB)
sollwertfloat = -18
schwelle = .2
blocklaenge = 2^25

If quelle = ziel Then 'change file name
	ziel = Left(ziel,InStrRev(ziel,".") - 1) + "_1" + Mid(ziel,InStrRev(ziel,"."))
EndIf

keinedatei = 0
maxamplitude = 0
geslinks = 0
gesrechts = 0
samples = 0
laengemerken = FileLen(quelle)
	
If quelle = "" Then
	End
EndIf

If Open(quelle For Binary Access Read As #1) Then
	Print "File not found"
	Sleep
	End
EndIf

Print #10, quelle;" "; '***
header = 0
Do 'seek 'data'
  header += 1
  Seek #1, header
  g = Input$(4, #1)
Loop Until g = "data"
header += 7
g = Input$(4, #1)
Print

Seek 1,1 'reset file pointer

'determine file parameters
g = Input(4, #1) 'skip the first 4 bytes
dateilaenge = Cvi(Input(4, #1)) 'file length
g = Input(8, #1) 'skip
subch1 = Cvi(Input(4, #1)) 'size of subchunk1
audioformat = CVShort(Input(2, #1)) 'audio format
kanaele = CVShort(Input(2, #1)) 'number of channels
samplerate = Cvi(Input(4, #1))
byterate = Cvi(Input(4, #1))
blockalign = CVShort(Input(2, #1))
bitspersample = CVShort(Input(2, #1))
g = Input(4, #1) 'skip
Seek 1,header - 3 
subch2 = Cvi(Input(4, #1))
					
gesamtsamples = (Lof(1)-header)/blockalign 'total number of samples
bloecke = Int(gesamtsamples * blockalign / blocklaenge) 'number of blocks (for input)
rest = gesamtsamples * blockalign - bloecke * blocklaenge

maske

'print title
Locate 1,1
Color weiss,schwarz
Print quelle
		
'print file parameters
Print
Print Tab(17); samplerate; " Hz"
Print Tab(17); bitspersample; " bit ";
Select Case audioformat
	Case 1
		Print"PCM"
	Case 3
		Print "float"
	Case Else
		Color hellrot,schwarz
		Print "*** Unbekanntes Audioformat ***"
		Color weiss,schwarz
		Sleep 2000
		Exit Select
End Select
	
Select Case kanaele
	Case 1
		Print Tab(18); "Mono"
	Case 2
		Print Tab(18); "Stereo"
	Case Else
		Print Tab(17); kanaele
End Select
	
dauer = subch2 / byterate
x = Int(dauer / 60)
g = Str$(dauer - 60 * x)
If InStr(g,".") = 2 Then
	g = "0" + g
EndIf
g = Str$(x) + ":" + g
Print Tab(18); Left$(g,InStr(g,".")+2)
    
'print file names
Print
Print 
Print Tab(18); quelle
Print Tab(18); ziel
			
Locate 15,1
Select Case audioformat
	Case 1 'pcm
		Print Tab(18); sollwertpcm; " dB"
	Case 3 'float
		Print Tab(18); sollwertfloat; " dB"
End Select
					
Do
	g = Input(blocklaenge, #1)
	Color weiss,schwarz
								
	Select Case audioformat 'determine actual value
		Case 1 'pcm
			samples += Len(g)/4	
			For gpp = Cast(Short Ptr,StrPtr(g)) To Cast(Short Ptr,StrPtr(g) + Len(g) - 1) Step 2
				
				'left value
				links = CSng(Abs(*gpp)) / ma 'convert to float and normalize
				If links > maxamplitude Then 'determine peak value
					maxamplitude = links
				EndIf
				geslinks += links * links
				
				'right value
				rechts = CSng(Abs(*(gpp+1))) / ma 'convert to float and normalize
			  If rechts > maxamplitude Then 'determine peak value
			  	maxamplitude = rechts
			  EndIf
			  gesrechts += rechts * rechts
			Next
						
		Case 3 'float
			samples += Len(g)/8
			For gpf = Cast(Single Ptr,StrPtr(g)) To Cast(Single Ptr,StrPtr(g) + Len(g) -1) Step 2 
				
				'right value
				rechts = Abs(*(gpf+1))
				If rechts > 1 Then
					rechts = 0
				EndIf
				If rechts > maxamplitude Then
			  	maxamplitude = rechts
			  EndIf
			  gesrechts += rechts * rechts
			  
			  'left value
				links = Abs(*gpf)
				If links > 1 Then
					links = 0 
				EndIf
				If links > maxamplitude Then
					maxamplitude = links
				EndIf
				geslinks += links * links
			Next
			
	End Select
			
Loop Until Len(g) < blocklaenge

'************************************************************************************
				
'compute amplification
Select Case audioformat
	Case 1 'pcm
		verst = vh(sollwertpcm) / (Sqr((geslinks + gesrechts) / (2*samples)))  
	Case 3 'float
		verst = vh(sollwertfloat) / (Sqr((geslinks + gesrechts) / (2*samples)))
End Select

'print actual value
Locate 16,18,0
Print Int(10 * (db(Sqr(geslinks /samples)))) / 10;" dB"
Locate 17,18,0
Print Int(10 * (db(Sqr(gesrechts/samples)))) / 10;" dB"
Locate 18,18,0
Print Int(10 * (db(Sqr((geslinks + gesrechts) / (2 * samples))))) / 10;" dB"
						
Close #1
										
If maxamplitude * verst > 1 Then 'avoid distorsion
	Locate 22,1,0
	Print "Die Verstärkung wurde um ";_
	      CInt(Abs(db(maxamplitude * verst))*100)/100;_
	      "dB abgesenkt,"
	Print "um eine Übersteuerung zu verhindern"
	verst = verst / (maxamplitude * verst)
EndIf
										
'print amplification factor
Locate 19,18,0
Print db(verst);" dB"
						
If Abs(db(verst)) > schwelle Then 'correct volume
	Open quelle For Binary Access Read As #1
	Open ziel For Output As #2
	Print #10, Lof(1);" "; '***
	'transfer header
	Print #2, Input$(header, #1);
	
	'transfer audio samples with new volume
	Do
		g = Input(blocklaenge, #1)
										
	  Select Case audioformat 'compute audio samples
	  	Case 1 'pcm
	  		For gpp = Cast(Short Ptr,StrPtr(g)) To Cast(Short Ptr,StrPtr(g) + Len(g) - 1)
					*gpp *= verst
				Next
	  		Print #2, g;
	  				  		
	  	Case 3 'float
	  		For gpf = Cast(Single Ptr,StrPtr(g)) To Cast(Single Ptr,StrPtr(g) + Len(g) - 1)
					*gpf *= verst
				Next 
				Print #2, g;
	  End Select		
	Loop Until Len(g) < blocklaenge
	
	Close #1
	Close #2
Else 'copy file
	Locate 13,6,0
	Color hellcyan,schwarz
	Print "   kopieren ";
	Color weiss,schwarz
	FileCopy(quelle, ziel)
EndIf

Locate 13,1,0
Print "                "
				
Close
Sleep
			
Sub maske
	
	View Print 1 To 12
	Cls
	View Print 14 To 25
	Cls
	Color hellgrau,schwarz
	View Print
		
  Locate 1,1,0
	Print
	Print
	Print "      Samplerate"
	Print "          Format"
	Print "          Kanäle"
	Print "           Dauer"
	Print
	Print
	Print "Quellverzeichnis"
	Print " Zielverzeichnis"
	Print "           Datei"
	Print
	Print Tab(18); String(50, "°")
	Print
	Print "       Sollpegel"
	Print "     Pegel links"
	Print "    Pegel rechts"
	Print "     Gesamtpegel"
	Print "     Verstärkung"
		
End Sub

Function vh (x As Single) As Single 'rechnet dB in verhältnis um
	
	Return Exp((x / 20) * Log(10))
	
End Function

Function db (x As Single) As Single 'rechnet verhältnis in dB um
	
	Return 20*(Log(x)/Log(10))
	
End Function
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: Sound file sample access (WAV or MP3)

Post by badidea »

I would say: 2 steps.
1. Read the files (of different formats) into memory in to a standard format. This is what sound libraries do as the output device (the thing that produces actual sound waves) expects a certain format. Or several formats (mono/stereo, 8 bit/16-bit), but at least a relative simple array [note1]. fbSound supports several formats, including wave and mp3.
2. Instead for playing the array of samples, process the array with your spectogram tool.

I just posted something similar here (just plotting the samples, no FFT): https://freebasic.net/forum/viewtopic.p ... 66#p264766 This library however does not support mp3.

[note1] For 2 channels (stereo), the left and right channel are interleaved. For your program probably not wanted. You might want to convert in to 2 separate arrays (left & right), or to 1 'mono' array (average/sum of left and right). Maybe also to floating point (with value -1...+1).
Sisophon2001
Posts: 1706
Joined: May 27, 2005 6:34
Location: Cambodia, Thailand, Lao, Ireland etc.
Contact:

Re: Sound file sample access (WAV or MP3)

Post by Sisophon2001 »

Thanks to both badidea and grindstone for your help. I will study the examples you shared.
Gromow
Posts: 9
Joined: May 03, 2019 14:55

Re: Sound file sample access (WAV or MP3)

Post by Gromow »

My friend advised me this service the online file converter https://onlineconvertfree.com/ and I was with satisfied.
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

Re: Sound file sample access (WAV or MP3)

Post by srvaldez »

@Gromow
I would like to know whether you are a human or a BOT, we had several BOT posting lately and your posts look suspicious
Gromow
Posts: 9
Joined: May 03, 2019 14:55

Re: Sound file sample access (WAV or MP3)

Post by Gromow »

srvaldez wrote:@Gromow
I would like to know whether you are a human or a BOT, we had several BOT posting lately and your posts look suspicious
I'm a human.
Post Reply