Rozdzielczość bitowa
Rozdzielczość bitowa określa dokładność, z jaką zapisana jest jedna próbka danych, określając ilość dostępnych dla niej bitów. Najpopularniejsze aktualnie formaty danych dźwiękowych opierają się na 16-
lub 24-
bitowych próbkach. Rzadziej spotykane są formaty 8-
lub 32-
bitowe. Na zajęciach będzie się zdarzać, że będziemy pracować i prowadzić badania na sztucznie tworzonych formatach o mniejszej ilości bitów, która nie będzie obsługiwana domyślnie przez biblioteki, a to będzie wymagało pewnych dodatkowych operacji.
Rozdzielczość bitowa określa zakres wartości, jakie mogą wystąpić we wczytywanym pliku. Owszem można wymusić zawsze jednolity tryb wczytywania danych (np. dtype=np.float32
), ale nie zmienia to, z jaką dokładnością zapisane są dane oraz z jakim domyślnym typem są one wczytane. Domyślnie dla poszczególnych formatów dane wczytane powinny być w poniższych typach:
- 8-bitowe - typ
uint8
- Uwaga! wartość neutralna sygnału w tym przypadku oscyluje w okolicach128
nie0
- 16-bitowe - typ
int16
- 24-bitowe i 32-bitowe - typ
int32
lubfloat32
Jeżeli w jakimś przypadku macie problem z odtworzeniem lub zapisaniem dźwięku, bo przykładowo słychać tylko szumy, to prawdopodobnie jest problem z interpretacją typu i trzeba wymusić inny typ danych na zmiennej .astype()
.
Zmiana typów danych
W przypadku obrazu głównie będziemy operować na typach danych bez znaku (kategoria \(B\)):
uint8
- tutaj zakres będzie \(\in <0,255>\),uint16
- tutaj zakres będzie \(\in <0,65535>\),float32
- tutaj zakres będzie \(\in <0,1>\).
Żeby między nimi się przemieszczać, wystarczy podzielić lub pomnożyć przez maksymalną wartość. Czyli żeby z uint8
przejść na float32
, należy wartości podzielić przez \(255\) (pierwszy wzór), a dla operacji odwrotnej pomnożyć przez tyle (drugi wzór).
W przypadku dźwięku sytuacja jest trochę bardziej skomplikowana, ponieważ tu częściej spotykamy się z większą ilością typów danych i nie są one tak konsekwentne:
uint8
- tutaj zakres będzie \(\in <0,255>\),int8
- tutaj zakres będzie \(\in <-128,127>\),int16
- tutaj zakres będzie \(\in <-32768,32767>\),int32
- tutaj zakres będzie \(\in <-2147483648,2147483647>\),float32
- tutaj zakres będzie \(\in <-1,1>\).
Jeżeli będziecie potrzebować kiedyś informacjo o typie zmiennych i zakresach ich wartości można je sprawdzić za pomocą funkcji np.iinfo
.
Jeżeli chodzi o przechodzenie pomiędzy zakresami tutaj najważniejsze jest na samym początku z zakresu typu \(C\) (wszystkie poza uint8
) na zakres typu \(B\) (a najlepiej \(A\)). Zauważcie, że środek zakresu wartości dla liczb całkowitych nie wypada idealnie na \(0\) i jeżeli będziecie starali się dokonywać operacji w ten sposób, będzie prowadziło to do różnego rodzaju błędów. To przesunięcie najłatwiej dokonać poprzez dodanie minimalnej wartości. W każdym z tych przypadków dalej możemy stosować oryginalne wzory.
Przykład przejść z float32
na int16
i na odwrót
Zaczynamy ze zmienną \(F_{<-1,1>}\) na float32
. W pierwszym kroku musimy przejść z zakresu \(\in <-1,1>\) na zakres \(\in <0,1>\). Wykonujemy to przy użyciu równania pierwszego równania: \[Z_A=\frac{Z_C-m}{n-m}\] \(Z_c\) będzie w naszym przypadku \(F_{<-1,1>}\), czyli nasze \(m=-1\), a \(n=1\). Całość przypiszemy sobie do zmiennej \(F_{<0,1>}\), żeby pamiętać na jakim zakresie wartości jesteśmy. Nasza operacja powinna wyglądać mniej więcej tak: \[F_{<0,1>}=\frac{F_{<-1,1>}-(-1)}{1-(-1)}=\frac{F+1}{2}\] Teraz musimy przejść na docelowy zakres int16
, stosując trzeci wzór: \[ Z_C=[Z_A*(n-m)]+m \] Nasze nowe \(m=-32768\), \(n=32767\), a zmienną nazwijmy \(F_{<-32768,32767>}\), czyli nasza operacja powinna wyglądać tak: \[ F_{<-32768,32767>}=[F_{<0,1>}*(32767-(-32768))]+(-32768) = F_{<0,1>} *65535 - 32768\] Teraz wystarczy przejść z float32
na int16
, czyli zmienić typ: \[I_{<-32768,32767>}=F_{<-32768,32767>}\] Żeby wykonać operację odwrotną należ wykonać, wszystkie kroki w odwrotnej kolejności. Czyli zaczynamy od przejścia z int16
na float32
: \[F_{<-32768,32767>}=I_{<-32768,32767>}\] Teraz przechodzimy z zakresu \(F_{<-32768,32767>}\) na zakres \(F_{<0,1>}\). \[F_{<0,1>}=\frac{F_{<-32768,32767>}-(-32768)}{32767-(-32768)}=\frac{F_{<-32768,32767>}+32768}{65535}\] Kolejny krok to przejście z zakresu \(F_{<0,1>}\) na \(F_{<-1,1>}\). \[F_{<-1,1>}=[F_{<0,1>} *(1-(-1))]+(-1) = F_{<0,1>}* 2 - 1\]