Tạo Shazam trong Java
Một vài ngày trước, tôi bắt gặp bài viết này: Làm thế nào Shazam Làm việc
này cũng làm tôi quan tâm đến việc làm thế nào một chương trình như Shazam hoạt động ... Và quan trọng hơn, làm thế nào cứng là nó để chương trình một cái gì đó tương tự như trong Java?
Về Shazam
Shazam là một ứng dụng mà bạn có thể sử dụng để phân tích / trận đấu âm nhạc. Khi bạn cài đặt nó trên điện thoại của bạn, và giữ microphone một số âm nhạc trong khoảng 20-30 giây, nó sẽ cho bạn biết bài hát nó được.
Khi tôi lần đầu tiên sử dụng nó nó đã cho tôi một cảm giác kỳ diệu. "Làm thế nào mà nó làm điều đó !?". Và ngay cả ngày hôm nay, sau khi sử dụng nó rất nhiều, nó vẫn có một chút cảm giác huyền diệu với nó.
Nó sẽ không thể tuyệt vời nếu chúng tôi có thể lập trình một cái gì đó của riêng của chúng tôi cung cấp cho cảm giác giống nhau không? Đó là mục tiêu của tôi cho ngày cuối tuần qua.
Nghe lên ..!
Trước tiên, có mẫu âm nhạc để phân tích đầu tiên chúng ta cần lắng nghe microphone trong ứng dụng Java của chúng tôi ...! Đây là điều tôi đã không thực hiện được nêu trong Java, vì vậy tôi không có ý tưởng như thế nào khó khăn này đã có được.
Nhưng hóa ra nó là rất dễ dàng:
định dạng AudioFormat thức = getFormat (); // Điền AudioFormat với các thiết lập truy nã
DataLine.Info info = mới DataLine.Info (TargetDataLine.class, định dạng);
thức dòng TargetDataLine = (TargetDataLine) AudioSystem.getLine (thông tin);
line.open (định dạng);
line.start ( );
Bây giờ chúng ta có thể đọc dữ liệu từ TargetDataLine giống như một InputStream bình thường:
// Trong thread khác tôi bắt đầu:
OutputStream ra = new ByteArrayOutputStream ();
chạy = true;
try {
while (chạy) {
int count = line.read (buffer, 0, buffer.length);
if (count> 0) {
out.write (buffer, 0, số);
}
}
out.close ();
} catch (IOException e) {
System.err.println ( " I / O vấn đề: "+ e);
System.exit (-1);
}
Sử dụng phương pháp này nó rất dễ dàng để mở micro và ghi lại tất cả các âm thanh! Các AudioFormat Tôi hiện đang sử dụng là:
tin AudioFormat getFormat () {
float sampleRate = 44100;
int sampleSizeInBits = 8;
int kênh = 1; // mono
boolean ký = true;
boolean bigEndian = true;
trở AudioFormat mới (sampleRate, sampleSizeInBits, kênh, ký kết, bigEndian);
}
Vì vậy, bây giờ chúng tôi có các dữ liệu được ghi lại trong một ByteArrayOutputStream, tuyệt vời! Bước 1 hoàn tất.
Dữ liệu Microphone
Thách thức tiếp theo là phân tích các dữ liệu, khi tôi xuất ra các dữ liệu tôi nhận được trong mảng byte của tôi, tôi có một danh sách dài những con số, như thế này:
0
0
1
2
4
7
6
3
-1
-2
-4
-2
-5
-7
-8
(vv)
Erhm ... đúng? Đây là âm thanh?
Để xem dữ liệu có thể được hình dung tôi đã lấy ra và đặt nó trong Open Office để tạo ra một biểu đồ đường:
đồ thị
Ah vâng! Kiểu này trông giống như 'âm thanh'. Dường như những gì bạn nhìn thấy khi sử dụng ví dụ Windows Sound Recorder.
Những thông tin này là thực sự được gọi là miền thời gian. Nhưng những con số này hiện nay về cơ bản là vô dụng đối với chúng ta ... nếu bạn đọc bài viết ở trên về cách Shazam hoạt động bạn sẽ đọc mà họ sử dụng một phân tích quang phổ thay vì dữ liệu miền thời gian trực tiếp.
Vì vậy, câu hỏi lớn tiếp theo là: Làm thế nào chúng ta chuyển đổi hiện nay dữ liệu vào một phân tích quang phổ?
Fourier rời rạc biến
để chuyển dữ liệu của chúng tôi vào dữ liệu có thể sử dụng chúng ta cần phải áp dụng cái gọi là rời rạc Fourier Transformation. Điều này lần lượt các dữ liệu từ miền thời gian sang miền tần số.
Chỉ có một vấn đề, nếu bạn chuyển dữ liệu vào miền tần số bạn mất mỗi bit thông tin liên quan đến thời gian. Vì vậy, bạn sẽ biết những gì tầm quan trọng của tất cả các tần số đang có, nhưng bạn không có ý tưởng khi chúng xuất hiện.
Để giải quyết điều này chúng ta cần một cửa sổ trượt. Chúng tôi có khối dữ liệu (trong trường hợp của tôi 4096 byte dữ liệu) và chuyển đổi chỉ là chút thông tin này. Sau đó, chúng ta biết được độ lớn của tất cả các tần số xảy ra trong quá trình này tồn tại chỉ 4096 byte.
Thực hiện điều này
Thay vì lo lắng về việc chuyển đổi Fourier Tôi googled một chút và thấy mã cho FFT để gọi (Fast Fourier Transformation). Tôi gọi mã này với các khối:
byte âm thanh [] = out.toByteArray ();
thức int totalSize = audio.length;
int amountPossible = totalSize / Harvester.CHUNK_SIZE;
// Khi chuyển vào miền tần số, chúng tôi sẽ cần phải phức tạp số:
Complex [] [] kết quả = new Complex [amountPossible] [];
// đối với tất cả các khối:
for (int lần 0 =; lần <amountPossible; lần ++) {
Complex [] phức tạp = new Complex [Harvester.CHUNK_SIZE] ;
for (int i = 0; i <Harvester.CHUNK_SIZE; i ++) {
// Đặt các dữ liệu miền thời gian vào một số phức với phần ảo là 0:
phức tạp [i] = new Complex (audio [(lần * Harvester.CHUNK_SIZE ) + i], 0);
}
// Thực hiện phân tích FFT trên đoạn:
kết quả [lần] = FFT.fft (phức tạp);
}
// Xong!
Bây giờ chúng ta có một mảng hai có chứa tất cả các khối như Complex []. Mảng này chứa dữ liệu về tất cả các tần số. Để hình dung dữ liệu này, tôi quyết định thực hiện một phân tích quang phổ đầy đủ (chỉ để chắc chắn rằng tôi có các toán phải).
Để hiển thị các dữ liệu tôi bị hack này với nhau:
for (int i = 0; i <results.length; i ++) {
int freq = 1;
for (int dòng = 1; dòng <size; dòng ++) {
// để có được độ lớn của âm thanh ở một tần số nhất định lát
// có được abs () từ các số phức.
đang được dịch, vui lòng đợi..
